draftly 1.0.7 → 2.0.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 (79) hide show
  1. package/README.md +12 -0
  2. package/dist/chunk-3T55CBNZ.cjs +33 -0
  3. package/dist/chunk-3T55CBNZ.cjs.map +1 -0
  4. package/dist/chunk-5MC4T7JH.cjs +58 -0
  5. package/dist/chunk-5MC4T7JH.cjs.map +1 -0
  6. package/dist/{chunk-72ZYRGRT.cjs → chunk-BWJLMREN.cjs} +11 -9
  7. package/dist/chunk-BWJLMREN.cjs.map +1 -0
  8. package/dist/{chunk-KBQDZ5IW.cjs → chunk-CLW73JRX.cjs} +100 -75
  9. package/dist/chunk-CLW73JRX.cjs.map +1 -0
  10. package/dist/{chunk-DFQYXFOP.js → chunk-EEHILRG5.js} +26 -3
  11. package/dist/chunk-EEHILRG5.js.map +1 -0
  12. package/dist/{chunk-HPSMS2WB.js → chunk-I563H35S.js} +101 -75
  13. package/dist/chunk-I563H35S.js.map +1 -0
  14. package/dist/chunk-IAXF4SJL.js +55 -0
  15. package/dist/chunk-IAXF4SJL.js.map +1 -0
  16. package/dist/chunk-JF3WXXMJ.js +31 -0
  17. package/dist/chunk-JF3WXXMJ.js.map +1 -0
  18. package/dist/{chunk-N3WL3XPB.js → chunk-L2XSK57Y.js} +1761 -478
  19. package/dist/chunk-L2XSK57Y.js.map +1 -0
  20. package/dist/{chunk-KDEDLC3D.cjs → chunk-TBVZEK2H.cjs} +27 -2
  21. package/dist/chunk-TBVZEK2H.cjs.map +1 -0
  22. package/dist/{chunk-2B3A3VSQ.cjs → chunk-W5ALMXG2.cjs} +1808 -504
  23. package/dist/chunk-W5ALMXG2.cjs.map +1 -0
  24. package/dist/{chunk-CG4M4TC7.js → chunk-ZUI3GI3W.js} +7 -5
  25. package/dist/chunk-ZUI3GI3W.js.map +1 -0
  26. package/dist/{draftly-BLnx3uGX.d.cts → draftly-BBL-AdOl.d.cts} +5 -1
  27. package/dist/{draftly-BLnx3uGX.d.ts → draftly-BBL-AdOl.d.ts} +5 -1
  28. package/dist/editor/index.cjs +22 -14
  29. package/dist/editor/index.d.cts +2 -1
  30. package/dist/editor/index.d.ts +2 -1
  31. package/dist/editor/index.js +2 -2
  32. package/dist/index.cjs +65 -39
  33. package/dist/index.d.cts +6 -3
  34. package/dist/index.d.ts +6 -3
  35. package/dist/index.js +6 -4
  36. package/dist/lib/index.cjs +12 -0
  37. package/dist/lib/index.cjs.map +1 -0
  38. package/dist/lib/index.d.cts +16 -0
  39. package/dist/lib/index.d.ts +16 -0
  40. package/dist/lib/index.js +3 -0
  41. package/dist/lib/index.js.map +1 -0
  42. package/dist/plugins/index.cjs +27 -17
  43. package/dist/plugins/index.d.cts +144 -9
  44. package/dist/plugins/index.d.ts +144 -9
  45. package/dist/plugins/index.js +5 -3
  46. package/dist/preview/index.cjs +16 -11
  47. package/dist/preview/index.d.cts +19 -4
  48. package/dist/preview/index.d.ts +19 -4
  49. package/dist/preview/index.js +3 -2
  50. package/package.json +8 -1
  51. package/src/editor/draftly.ts +1 -0
  52. package/src/editor/plugin.ts +5 -1
  53. package/src/editor/theme.ts +1 -0
  54. package/src/editor/utils.ts +31 -0
  55. package/src/index.ts +5 -4
  56. package/src/lib/index.ts +2 -0
  57. package/src/lib/input-handler.ts +45 -0
  58. package/src/plugins/code-plugin.theme.ts +426 -0
  59. package/src/plugins/code-plugin.ts +810 -561
  60. package/src/plugins/emoji-plugin.ts +140 -0
  61. package/src/plugins/index.ts +63 -57
  62. package/src/plugins/inline-plugin.ts +305 -291
  63. package/src/plugins/math-plugin.ts +12 -0
  64. package/src/plugins/table-plugin.ts +900 -0
  65. package/src/preview/context.ts +4 -1
  66. package/src/preview/css-generator.ts +14 -1
  67. package/src/preview/index.ts +9 -1
  68. package/src/preview/preview.ts +2 -1
  69. package/src/preview/renderer.ts +21 -20
  70. package/src/preview/syntax-theme.ts +110 -0
  71. package/src/preview/types.ts +14 -0
  72. package/dist/chunk-2B3A3VSQ.cjs.map +0 -1
  73. package/dist/chunk-72ZYRGRT.cjs.map +0 -1
  74. package/dist/chunk-CG4M4TC7.js.map +0 -1
  75. package/dist/chunk-DFQYXFOP.js.map +0 -1
  76. package/dist/chunk-HPSMS2WB.js.map +0 -1
  77. package/dist/chunk-KBQDZ5IW.cjs.map +0 -1
  78. package/dist/chunk-KDEDLC3D.cjs.map +0 -1
  79. package/dist/chunk-N3WL3XPB.js.map +0 -1
@@ -1,7 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var chunk72ZYRGRT_cjs = require('./chunk-72ZYRGRT.cjs');
4
- var chunkKDEDLC3D_cjs = require('./chunk-KDEDLC3D.cjs');
3
+ var chunkBWJLMREN_cjs = require('./chunk-BWJLMREN.cjs');
4
+ var chunk3T55CBNZ_cjs = require('./chunk-3T55CBNZ.cjs');
5
+ var chunkCLW73JRX_cjs = require('./chunk-CLW73JRX.cjs');
6
+ var chunkTBVZEK2H_cjs = require('./chunk-TBVZEK2H.cjs');
5
7
  var view = require('@codemirror/view');
6
8
  var language = require('@codemirror/language');
7
9
  var highlight = require('@lezer/highlight');
@@ -10,16 +12,36 @@ var katex = require('katex');
10
12
  var katexCss = require('katex/dist/katex.min.css?raw');
11
13
  var mermaid = require('mermaid');
12
14
  var languageData = require('@codemirror/language-data');
15
+ var emoji = require('node-emoji');
13
16
 
14
17
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
18
 
19
+ function _interopNamespace(e) {
20
+ if (e && e.__esModule) return e;
21
+ var n = Object.create(null);
22
+ if (e) {
23
+ Object.keys(e).forEach(function (k) {
24
+ if (k !== 'default') {
25
+ var d = Object.getOwnPropertyDescriptor(e, k);
26
+ Object.defineProperty(n, k, d.get ? d : {
27
+ enumerable: true,
28
+ get: function () { return e[k]; }
29
+ });
30
+ }
31
+ });
32
+ }
33
+ n.default = e;
34
+ return Object.freeze(n);
35
+ }
36
+
16
37
  var DOMPurify__default = /*#__PURE__*/_interopDefault(DOMPurify);
17
38
  var katex__default = /*#__PURE__*/_interopDefault(katex);
18
39
  var katexCss__default = /*#__PURE__*/_interopDefault(katexCss);
19
40
  var mermaid__default = /*#__PURE__*/_interopDefault(mermaid);
41
+ var emoji__namespace = /*#__PURE__*/_interopNamespace(emoji);
20
42
 
21
43
  // src/plugins/paragraph-plugin.ts
22
- var ParagraphPlugin = class extends chunk72ZYRGRT_cjs.DraftlyPlugin {
44
+ var ParagraphPlugin = class extends chunkBWJLMREN_cjs.DraftlyPlugin {
23
45
  name = "paragraph";
24
46
  version = "1.0.0";
25
47
  requiredNodes = ["Paragraph"];
@@ -36,7 +58,7 @@ var ParagraphPlugin = class extends chunk72ZYRGRT_cjs.DraftlyPlugin {
36
58
  return `<p class="cm-draftly-paragraph">${children}</p>`;
37
59
  }
38
60
  };
39
- var theme = chunkKDEDLC3D_cjs.createTheme({
61
+ var theme = chunkTBVZEK2H_cjs.createTheme({
40
62
  default: {
41
63
  ".cm-draftly-paragraph": {
42
64
  paddingTop: "0.5em",
@@ -63,7 +85,7 @@ var headingLineDecorations = {
63
85
  "heading-5": view.Decoration.line({ class: "cm-draftly-line-h5" }),
64
86
  "heading-6": view.Decoration.line({ class: "cm-draftly-line-h6" })
65
87
  };
66
- var HeadingPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
88
+ var HeadingPlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
67
89
  name = "heading";
68
90
  version = "1.0.0";
69
91
  decorationPriority = 10;
@@ -135,7 +157,7 @@ var HeadingPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
135
157
  `;
136
158
  }
137
159
  };
138
- var theme2 = chunkKDEDLC3D_cjs.createTheme({
160
+ var theme2 = chunkTBVZEK2H_cjs.createTheme({
139
161
  default: {
140
162
  ".cm-draftly-h1": {
141
163
  fontSize: "2em",
@@ -234,7 +256,7 @@ var highlightParser = {
234
256
  );
235
257
  }
236
258
  };
237
- var InlinePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
259
+ var InlinePlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
238
260
  name = "inline";
239
261
  version = "1.0.0";
240
262
  decorationPriority = 20;
@@ -271,36 +293,47 @@ var InlinePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
271
293
  return [
272
294
  {
273
295
  key: "Mod-b",
274
- run: chunkKDEDLC3D_cjs.toggleMarkdownStyle("**"),
296
+ run: chunkTBVZEK2H_cjs.toggleMarkdownStyle("**"),
275
297
  preventDefault: true
276
298
  },
277
299
  {
278
300
  key: "Mod-i",
279
- run: chunkKDEDLC3D_cjs.toggleMarkdownStyle("*"),
301
+ run: chunkTBVZEK2H_cjs.toggleMarkdownStyle("*"),
280
302
  preventDefault: true
281
303
  },
282
304
  {
283
305
  key: "Mod-Shift-s",
284
- run: chunkKDEDLC3D_cjs.toggleMarkdownStyle("~~"),
306
+ run: chunkTBVZEK2H_cjs.toggleMarkdownStyle("~~"),
285
307
  preventDefault: true
286
308
  },
287
309
  {
288
310
  key: "Mod-,",
289
- run: chunkKDEDLC3D_cjs.toggleMarkdownStyle("~"),
311
+ run: chunkTBVZEK2H_cjs.toggleMarkdownStyle("~"),
290
312
  preventDefault: true
291
313
  },
292
314
  {
293
315
  key: "Mod-.",
294
- run: chunkKDEDLC3D_cjs.toggleMarkdownStyle("^"),
316
+ run: chunkTBVZEK2H_cjs.toggleMarkdownStyle("^"),
295
317
  preventDefault: true
296
318
  },
297
319
  {
298
320
  key: "Mod-Shift-h",
299
- run: chunkKDEDLC3D_cjs.toggleMarkdownStyle("=="),
321
+ run: chunkTBVZEK2H_cjs.toggleMarkdownStyle("=="),
300
322
  preventDefault: true
301
323
  }
302
324
  ];
303
325
  }
326
+ /**
327
+ * Intercepts inline marker typing to wrap selected text.
328
+ *
329
+ * If user types inline markers while text is selected, wraps each selected
330
+ * range with the appropriate marker:
331
+ * - * _ ~ ^ -> marker + selected + marker
332
+ * - = -> ==selected==
333
+ */
334
+ getExtensions() {
335
+ return [chunk3T55CBNZ_cjs.createWrapSelectionInputHandler({ "*": "*", _: "_", "~": "~", "^": "^", "=": "==" })];
336
+ }
304
337
  /**
305
338
  * Return markdown parser extensions for highlight syntax (==text==)
306
339
  */
@@ -372,7 +405,7 @@ var InlinePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
372
405
  return `<span class="${className}">${children}</span>`;
373
406
  }
374
407
  };
375
- var theme3 = chunkKDEDLC3D_cjs.createTheme({
408
+ var theme3 = chunkTBVZEK2H_cjs.createTheme({
376
409
  default: {
377
410
  // Emphasis (italic)
378
411
  ".cm-draftly-emphasis": {
@@ -468,7 +501,7 @@ var LinkTooltipWidget = class extends view.WidgetType {
468
501
  return event.type !== "click" && event.type !== "mouseenter" && event.type !== "mouseleave";
469
502
  }
470
503
  };
471
- var LinkPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
504
+ var LinkPlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
472
505
  name = "link";
473
506
  version = "1.0.0";
474
507
  decorationPriority = 22;
@@ -672,7 +705,7 @@ var LinkTextWidget = class extends view.WidgetType {
672
705
  return event.type !== "click" && event.type !== "mouseenter" && event.type !== "mouseleave";
673
706
  }
674
707
  };
675
- var theme4 = chunkKDEDLC3D_cjs.createTheme({
708
+ var theme4 = chunkTBVZEK2H_cjs.createTheme({
676
709
  default: {
677
710
  // Link text
678
711
  ".cm-draftly-link-text": {
@@ -818,7 +851,7 @@ var TaskCheckboxWidget = class extends view.WidgetType {
818
851
  }
819
852
  }
820
853
  };
821
- var ListPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
854
+ var ListPlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
822
855
  name = "list";
823
856
  version = "1.0.0";
824
857
  decorationPriority = 20;
@@ -1021,7 +1054,7 @@ var ListPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
1021
1054
  }
1022
1055
  }
1023
1056
  };
1024
- var theme5 = chunkKDEDLC3D_cjs.createTheme({
1057
+ var theme5 = chunkTBVZEK2H_cjs.createTheme({
1025
1058
  default: {
1026
1059
  // Indentation marker positioning
1027
1060
  ".cm-draftly-list-indent": {
@@ -1119,6 +1152,653 @@ var theme5 = chunkKDEDLC3D_cjs.createTheme({
1119
1152
  }
1120
1153
  }
1121
1154
  });
1155
+ function parseAlignment(cell) {
1156
+ const trimmed = cell.trim();
1157
+ const left = trimmed.startsWith(":");
1158
+ const right = trimmed.endsWith(":");
1159
+ if (left && right) return "center";
1160
+ if (right) return "right";
1161
+ return "left";
1162
+ }
1163
+ function parseTableMarkdown(markdown) {
1164
+ const lines = markdown.split("\n").filter((l) => l.trim().length > 0);
1165
+ if (lines.length < 2) return null;
1166
+ const parseCells = (line) => {
1167
+ let trimmed = line.trim();
1168
+ if (trimmed.startsWith("|")) trimmed = trimmed.slice(1);
1169
+ if (trimmed.endsWith("|")) trimmed = trimmed.slice(0, -1);
1170
+ return trimmed.split("|").map((c) => c.trim());
1171
+ };
1172
+ const headers = parseCells(lines[0]);
1173
+ const delimiterCells = parseCells(lines[1]);
1174
+ const isDelimiter = delimiterCells.every((c) => /^:?-+:?$/.test(c.trim()));
1175
+ if (!isDelimiter) return null;
1176
+ const alignments = delimiterCells.map(parseAlignment);
1177
+ const rows = [];
1178
+ for (let i = 2; i < lines.length; i++) {
1179
+ rows.push(parseCells(lines[i]));
1180
+ }
1181
+ return { headers, alignments, rows };
1182
+ }
1183
+ function isRowEmpty(rowText) {
1184
+ const trimmed = rowText.trim();
1185
+ if (!trimmed.startsWith("|")) return false;
1186
+ let inner = trimmed;
1187
+ if (inner.startsWith("|")) inner = inner.slice(1);
1188
+ if (inner.endsWith("|")) inner = inner.slice(0, -1);
1189
+ return inner.split("|").every((cell) => cell.trim() === "");
1190
+ }
1191
+ async function renderCellWithPreviewRenderer(text, config) {
1192
+ if (!text.trim()) {
1193
+ return "&nbsp;";
1194
+ }
1195
+ const renderer = new chunkCLW73JRX_cjs.PreviewRenderer(
1196
+ text,
1197
+ config?.plugins || [],
1198
+ config?.markdown || [],
1199
+ config?.theme || "auto" /* AUTO */,
1200
+ true
1201
+ );
1202
+ const html = await renderer.render();
1203
+ const paragraphMatch = html.match(/^\s*<p>([\s\S]*)<\/p>\s*$/i);
1204
+ if (paragraphMatch && paragraphMatch[1] !== void 0) {
1205
+ return paragraphMatch[1];
1206
+ }
1207
+ return html;
1208
+ }
1209
+ function getColumnAlignment(alignments, index) {
1210
+ return alignments[index] || "left";
1211
+ }
1212
+ function getColumnCount(headers, row) {
1213
+ return Math.max(headers.length, row.length);
1214
+ }
1215
+ async function renderTableToHtml(parsed, config) {
1216
+ const { headers, alignments, rows } = parsed;
1217
+ let html = '<div class="cm-draftly-table-widget">';
1218
+ html += '<table class="cm-draftly-table">';
1219
+ html += "<thead><tr>";
1220
+ for (let i = 0; i < headers.length; i++) {
1221
+ const cell = headers[i] || "";
1222
+ const align = getColumnAlignment(alignments, i);
1223
+ const rendered = await renderCellWithPreviewRenderer(cell, config);
1224
+ html += `<th style="text-align: ${align}">${rendered}</th>`;
1225
+ }
1226
+ html += "</tr></thead>";
1227
+ html += "<tbody>";
1228
+ for (const row of rows) {
1229
+ html += "<tr>";
1230
+ const colCount = getColumnCount(headers, row);
1231
+ for (let i = 0; i < colCount; i++) {
1232
+ const align = getColumnAlignment(alignments, i);
1233
+ const cell = row[i] || "";
1234
+ const rendered = await renderCellWithPreviewRenderer(cell, config);
1235
+ html += `<td style="text-align: ${align}">${rendered}</td>`;
1236
+ }
1237
+ html += "</tr>";
1238
+ }
1239
+ html += "</tbody>";
1240
+ html += "</table></div>";
1241
+ return html;
1242
+ }
1243
+ var TableWidget = class extends view.WidgetType {
1244
+ constructor(tableMarkdown, from, to, config) {
1245
+ super();
1246
+ this.tableMarkdown = tableMarkdown;
1247
+ this.from = from;
1248
+ this.to = to;
1249
+ this.config = config;
1250
+ }
1251
+ eq(other) {
1252
+ return other.tableMarkdown === this.tableMarkdown && other.from === this.from && other.to === this.to && other.config === this.config;
1253
+ }
1254
+ toDOM(view) {
1255
+ const wrapper = document.createElement("div");
1256
+ wrapper.className = "cm-draftly-table-widget";
1257
+ const parsed = parseTableMarkdown(this.tableMarkdown);
1258
+ if (!parsed) {
1259
+ wrapper.textContent = "[Invalid table]";
1260
+ return wrapper;
1261
+ }
1262
+ const { headers, alignments, rows } = parsed;
1263
+ const table = document.createElement("table");
1264
+ table.className = "cm-draftly-table";
1265
+ const thead = document.createElement("thead");
1266
+ const headerRow = document.createElement("tr");
1267
+ headers.forEach((h, i) => {
1268
+ const th = document.createElement("th");
1269
+ th.innerHTML = "&nbsp;";
1270
+ this.renderCellAsync(h, th);
1271
+ const align = alignments[i];
1272
+ if (align) th.style.textAlign = align;
1273
+ headerRow.appendChild(th);
1274
+ });
1275
+ thead.appendChild(headerRow);
1276
+ table.appendChild(thead);
1277
+ const tbody = document.createElement("tbody");
1278
+ rows.forEach((row) => {
1279
+ const tr = document.createElement("tr");
1280
+ const colCount = getColumnCount(headers, row);
1281
+ for (let i = 0; i < colCount; i++) {
1282
+ const td = document.createElement("td");
1283
+ td.innerHTML = "&nbsp;";
1284
+ this.renderCellAsync(row[i] || "", td);
1285
+ const align = getColumnAlignment(alignments, i);
1286
+ td.style.textAlign = align;
1287
+ tr.appendChild(td);
1288
+ }
1289
+ tbody.appendChild(tr);
1290
+ });
1291
+ table.appendChild(tbody);
1292
+ wrapper.appendChild(table);
1293
+ wrapper.addEventListener("click", (e) => {
1294
+ e.preventDefault();
1295
+ e.stopPropagation();
1296
+ view.dispatch({
1297
+ selection: { anchor: this.from },
1298
+ scrollIntoView: true
1299
+ });
1300
+ view.focus();
1301
+ });
1302
+ return wrapper;
1303
+ }
1304
+ /**
1305
+ * Render cell content asynchronously using PreviewRenderer
1306
+ */
1307
+ async renderCellAsync(text, element) {
1308
+ if (!text.trim()) {
1309
+ element.innerHTML = "&nbsp;";
1310
+ return;
1311
+ }
1312
+ try {
1313
+ element.innerHTML = await renderCellWithPreviewRenderer(text, this.config);
1314
+ } catch (error) {
1315
+ console.error("Failed to render table cell:", error);
1316
+ element.textContent = text;
1317
+ }
1318
+ }
1319
+ ignoreEvent(event) {
1320
+ return event.type !== "click";
1321
+ }
1322
+ };
1323
+ var tableMarkDecorations = {
1324
+ "table-line": view.Decoration.line({ class: "cm-draftly-table-line" }),
1325
+ "table-line-start": view.Decoration.line({ class: "cm-draftly-table-line-start" }),
1326
+ "table-line-end": view.Decoration.line({ class: "cm-draftly-table-line-end" }),
1327
+ "table-delimiter": view.Decoration.line({ class: "cm-draftly-table-delimiter-line" }),
1328
+ "table-rendered": view.Decoration.line({ class: "cm-draftly-table-rendered" }),
1329
+ // "table-hidden": Decoration.mark({ class: "cm-draftly-table-hidden" }),
1330
+ "table-hidden": view.Decoration.replace({})
1331
+ };
1332
+ var TablePlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
1333
+ name = "table";
1334
+ version = "1.0.0";
1335
+ decorationPriority = 20;
1336
+ requiredNodes = ["Table", "TableHeader", "TableDelimiter", "TableRow", "TableCell"];
1337
+ /** Configuration stored from onRegister */
1338
+ draftlyConfig;
1339
+ onRegister(context) {
1340
+ super.onRegister(context);
1341
+ this.draftlyConfig = context.config;
1342
+ }
1343
+ get theme() {
1344
+ return theme6;
1345
+ }
1346
+ // ============================================
1347
+ // Keymaps
1348
+ // ============================================
1349
+ getKeymap() {
1350
+ return [
1351
+ {
1352
+ key: "Mod-Shift-t",
1353
+ run: (view) => this.insertTable(view),
1354
+ preventDefault: true
1355
+ },
1356
+ {
1357
+ key: "Mod-Enter",
1358
+ run: (view) => this.addRow(view),
1359
+ preventDefault: true
1360
+ },
1361
+ {
1362
+ key: "Mod-Shift-Enter",
1363
+ run: (view) => this.addColumn(view),
1364
+ preventDefault: true
1365
+ },
1366
+ {
1367
+ key: "Enter",
1368
+ run: (view) => this.handleEnter(view)
1369
+ },
1370
+ {
1371
+ key: "Tab",
1372
+ run: (view) => this.handleTab(view, false)
1373
+ },
1374
+ {
1375
+ key: "Shift-Tab",
1376
+ run: (view) => this.handleTab(view, true)
1377
+ }
1378
+ ];
1379
+ }
1380
+ // ============================================
1381
+ // Decorations
1382
+ // ============================================
1383
+ buildDecorations(ctx) {
1384
+ const { view: view$1, decorations } = ctx;
1385
+ const tree = language.syntaxTree(view$1.state);
1386
+ tree.iterate({
1387
+ enter: (node) => {
1388
+ if (node.name !== "Table") return;
1389
+ const { from, to } = node;
1390
+ const nodeLineStart = view$1.state.doc.lineAt(from);
1391
+ const nodeLineEnd = view$1.state.doc.lineAt(to);
1392
+ const cursorInRange = ctx.selectionOverlapsRange(nodeLineStart.from, nodeLineEnd.to);
1393
+ if (cursorInRange) {
1394
+ for (let i = nodeLineStart.number; i <= nodeLineEnd.number; i++) {
1395
+ const line = view$1.state.doc.line(i);
1396
+ decorations.push(tableMarkDecorations["table-line"].range(line.from));
1397
+ if (i === nodeLineStart.number) {
1398
+ decorations.push(tableMarkDecorations["table-line-start"].range(line.from));
1399
+ }
1400
+ if (i === nodeLineEnd.number) {
1401
+ decorations.push(tableMarkDecorations["table-line-end"].range(line.from));
1402
+ }
1403
+ if (i === nodeLineStart.number + 1) {
1404
+ decorations.push(tableMarkDecorations["table-delimiter"].range(line.from));
1405
+ }
1406
+ }
1407
+ } else {
1408
+ const tableContent = view$1.state.sliceDoc(from, to);
1409
+ for (let i = nodeLineStart.number; i <= nodeLineEnd.number; i++) {
1410
+ const line = view$1.state.doc.line(i);
1411
+ decorations.push(tableMarkDecorations["table-rendered"].range(line.from));
1412
+ decorations.push(tableMarkDecorations["table-hidden"].range(line.from, line.to));
1413
+ }
1414
+ decorations.push(
1415
+ view.Decoration.widget({
1416
+ widget: new TableWidget(tableContent, from, to, this.draftlyConfig),
1417
+ side: 1,
1418
+ block: false
1419
+ }).range(to)
1420
+ );
1421
+ }
1422
+ }
1423
+ });
1424
+ }
1425
+ // ============================================
1426
+ // Keymap Handlers
1427
+ // ============================================
1428
+ /**
1429
+ * Insert a new 3×3 table at cursor position
1430
+ */
1431
+ insertTable(view) {
1432
+ const { state } = view;
1433
+ const cursor = state.selection.main.head;
1434
+ const line = state.doc.lineAt(cursor);
1435
+ const insertPos = line.text.trim() ? line.to : line.from;
1436
+ const template = [
1437
+ "| Header 1 | Header 2 | Header 3 |",
1438
+ "| -------- | -------- | -------- |",
1439
+ "| | | |"
1440
+ ].join("\n");
1441
+ const prefix = line.text.trim() ? "\n" : "";
1442
+ const suffix = "\n";
1443
+ view.dispatch({
1444
+ changes: {
1445
+ from: insertPos,
1446
+ insert: prefix + template + suffix
1447
+ },
1448
+ selection: {
1449
+ anchor: insertPos + prefix.length + 2
1450
+ // Position cursor in first header cell
1451
+ }
1452
+ });
1453
+ return true;
1454
+ }
1455
+ /**
1456
+ * Add a new row below the current row (Mod-Enter)
1457
+ */
1458
+ addRow(view) {
1459
+ const tableInfo = this.getTableAtCursor(view);
1460
+ if (!tableInfo) return false;
1461
+ const { state } = view;
1462
+ const cursor = state.selection.main.head;
1463
+ const currentLine = state.doc.lineAt(cursor);
1464
+ const parsed = parseTableMarkdown(state.sliceDoc(tableInfo.from, tableInfo.to));
1465
+ if (!parsed) return false;
1466
+ const colCount = parsed.headers.length;
1467
+ const emptyRow = "| " + Array.from({ length: colCount }, () => " ").join(" | ") + " |";
1468
+ view.dispatch({
1469
+ changes: {
1470
+ from: currentLine.to,
1471
+ insert: "\n" + emptyRow
1472
+ },
1473
+ selection: {
1474
+ anchor: currentLine.to + 3
1475
+ // Position in first cell of new row
1476
+ }
1477
+ });
1478
+ return true;
1479
+ }
1480
+ /**
1481
+ * Add a new column after the current column (Mod-Shift-Enter)
1482
+ */
1483
+ addColumn(view) {
1484
+ const tableInfo = this.getTableAtCursor(view);
1485
+ if (!tableInfo) return false;
1486
+ const { state } = view;
1487
+ const cursor = state.selection.main.head;
1488
+ const currentLine = state.doc.lineAt(cursor);
1489
+ const lineText = currentLine.text;
1490
+ const cursorInLine = cursor - currentLine.from;
1491
+ let colIndex = -1;
1492
+ for (let i = 0; i < cursorInLine; i++) {
1493
+ if (lineText[i] === "|") colIndex++;
1494
+ }
1495
+ colIndex = Math.max(0, colIndex);
1496
+ const tableText = state.sliceDoc(tableInfo.from, tableInfo.to);
1497
+ const lines = tableText.split("\n");
1498
+ const newLines = lines.map((line, lineIdx) => {
1499
+ const cells = this.splitLineToCells(line);
1500
+ const insertAfter = Math.min(colIndex, cells.length - 1);
1501
+ if (lineIdx === 1) {
1502
+ cells.splice(insertAfter + 1, 0, " -------- ");
1503
+ } else {
1504
+ cells.splice(insertAfter + 1, 0, " ");
1505
+ }
1506
+ return "|" + cells.join("|") + "|";
1507
+ });
1508
+ view.dispatch({
1509
+ changes: {
1510
+ from: tableInfo.from,
1511
+ to: tableInfo.to,
1512
+ insert: newLines.join("\n")
1513
+ }
1514
+ });
1515
+ return true;
1516
+ }
1517
+ /**
1518
+ * Handle Enter key inside a table.
1519
+ * - Last cell of last row: create a new row
1520
+ * - Empty last row: remove it and move cursor after table
1521
+ */
1522
+ handleEnter(view) {
1523
+ const tableInfo = this.getTableAtCursor(view);
1524
+ if (!tableInfo) return false;
1525
+ const { state } = view;
1526
+ const cursor = state.selection.main.head;
1527
+ const cursorLine = state.doc.lineAt(cursor);
1528
+ const tableEndLine = state.doc.lineAt(tableInfo.to);
1529
+ if (cursorLine.number !== tableEndLine.number) return false;
1530
+ const lineText = cursorLine.text;
1531
+ const cursorOffset = cursor - cursorLine.from;
1532
+ const pipes = [];
1533
+ for (let i = 0; i < lineText.length; i++) {
1534
+ if (lineText[i] === "|") pipes.push(i);
1535
+ }
1536
+ if (pipes.length < 2) return false;
1537
+ const lastCellStart = pipes[pipes.length - 2];
1538
+ if (cursorOffset < lastCellStart) return false;
1539
+ if (isRowEmpty(lineText)) {
1540
+ const removeFrom = cursorLine.from - 1;
1541
+ const removeTo = cursorLine.to;
1542
+ view.dispatch({
1543
+ changes: { from: Math.max(0, removeFrom), to: removeTo },
1544
+ selection: {
1545
+ anchor: Math.min(Math.max(0, removeFrom) + 1, view.state.doc.length)
1546
+ }
1547
+ });
1548
+ return true;
1549
+ }
1550
+ const parsed = parseTableMarkdown(state.sliceDoc(tableInfo.from, tableInfo.to));
1551
+ if (!parsed) return false;
1552
+ const colCount = parsed.headers.length;
1553
+ const emptyRow = "| " + Array.from({ length: colCount }, () => " ").join(" | ") + " |";
1554
+ view.dispatch({
1555
+ changes: {
1556
+ from: cursorLine.to,
1557
+ insert: "\n" + emptyRow
1558
+ },
1559
+ selection: {
1560
+ anchor: cursorLine.to + 3
1561
+ // Position in first cell of new row
1562
+ }
1563
+ });
1564
+ return true;
1565
+ }
1566
+ /**
1567
+ * Handle Tab key inside a table — move to next/previous cell
1568
+ */
1569
+ handleTab(view, backwards) {
1570
+ const tableInfo = this.getTableAtCursor(view);
1571
+ if (!tableInfo) return false;
1572
+ const { state } = view;
1573
+ const cursor = state.selection.main.head;
1574
+ const tableText = state.sliceDoc(tableInfo.from, tableInfo.to);
1575
+ const lines = tableText.split("\n");
1576
+ const cellPositions = [];
1577
+ for (let li = 0; li < lines.length; li++) {
1578
+ if (li === 1) continue;
1579
+ const line = lines[li];
1580
+ const lineFrom = tableInfo.from + lines.slice(0, li).reduce((sum, l) => sum + l.length + 1, 0);
1581
+ const pipes = [];
1582
+ for (let i = 0; i < line.length; i++) {
1583
+ if (line[i] === "|") pipes.push(i);
1584
+ }
1585
+ for (let p = 0; p < pipes.length - 1; p++) {
1586
+ const cellStart = pipes[p] + 1;
1587
+ const cellEnd = pipes[p + 1];
1588
+ cellPositions.push({
1589
+ lineFrom,
1590
+ start: cellStart,
1591
+ end: cellEnd
1592
+ });
1593
+ }
1594
+ }
1595
+ let currentCellIdx = -1;
1596
+ for (let i = 0; i < cellPositions.length; i++) {
1597
+ const cell = cellPositions[i];
1598
+ const absStart = cell.lineFrom + cell.start;
1599
+ const absEnd = cell.lineFrom + cell.end;
1600
+ if (cursor >= absStart && cursor <= absEnd) {
1601
+ currentCellIdx = i;
1602
+ break;
1603
+ }
1604
+ }
1605
+ if (currentCellIdx === -1) return false;
1606
+ const nextIdx = backwards ? currentCellIdx - 1 : currentCellIdx + 1;
1607
+ if (nextIdx < 0 || nextIdx >= cellPositions.length) return false;
1608
+ const nextCell = cellPositions[nextIdx];
1609
+ const cellText = state.sliceDoc(nextCell.lineFrom + nextCell.start, nextCell.lineFrom + nextCell.end);
1610
+ const trimStart = cellText.length - cellText.trimStart().length;
1611
+ const trimEnd = cellText.length - cellText.trimEnd().length;
1612
+ const selectFrom = nextCell.lineFrom + nextCell.start + (trimStart > 0 ? 1 : 0);
1613
+ const selectTo = nextCell.lineFrom + nextCell.end - (trimEnd > 0 ? 1 : 0);
1614
+ view.dispatch({
1615
+ selection: {
1616
+ anchor: selectFrom,
1617
+ head: selectTo
1618
+ },
1619
+ scrollIntoView: true
1620
+ });
1621
+ return true;
1622
+ }
1623
+ // ============================================
1624
+ // Helpers
1625
+ // ============================================
1626
+ /**
1627
+ * Find the Table node at the cursor position
1628
+ */
1629
+ getTableAtCursor(view) {
1630
+ const tree = language.syntaxTree(view.state);
1631
+ const cursor = view.state.selection.main.head;
1632
+ let result = null;
1633
+ tree.iterate({
1634
+ enter: (node) => {
1635
+ if (node.name === "Table" && cursor >= node.from && cursor <= node.to) {
1636
+ result = { from: node.from, to: node.to };
1637
+ }
1638
+ }
1639
+ });
1640
+ return result;
1641
+ }
1642
+ /**
1643
+ * Split a table line into cells (keeping the whitespace around content)
1644
+ */
1645
+ splitLineToCells(line) {
1646
+ let trimmed = line.trim();
1647
+ if (trimmed.startsWith("|")) trimmed = trimmed.slice(1);
1648
+ if (trimmed.endsWith("|")) trimmed = trimmed.slice(0, -1);
1649
+ return trimmed.split("|");
1650
+ }
1651
+ // ============================================
1652
+ // Preview Rendering
1653
+ // ============================================
1654
+ async renderToHTML(node, _children, _ctx) {
1655
+ if (node.name === "Table") {
1656
+ const content = _ctx.sliceDoc(node.from, node.to);
1657
+ const parsed = parseTableMarkdown(content);
1658
+ if (!parsed) return null;
1659
+ return await renderTableToHtml(parsed, this.draftlyConfig);
1660
+ }
1661
+ if (node.name === "TableHeader" || node.name === "TableDelimiter" || node.name === "TableRow" || node.name === "TableCell") {
1662
+ return "";
1663
+ }
1664
+ return null;
1665
+ }
1666
+ };
1667
+ var theme6 = chunkTBVZEK2H_cjs.createTheme({
1668
+ default: {
1669
+ // Raw table lines — monospace when cursor is inside
1670
+ ".cm-draftly-table-line": {
1671
+ "--radius": "0.375rem",
1672
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
1673
+ fontSize: "0.9rem",
1674
+ backgroundColor: "rgba(0, 0, 0, 0.02)",
1675
+ padding: "0 0.75rem !important",
1676
+ lineHeight: "1.6",
1677
+ borderLeft: "1px solid var(--color-border, #e2e8f0)",
1678
+ borderRight: "1px solid var(--color-border, #e2e8f0)"
1679
+ },
1680
+ ".cm-draftly-table-line-start": {
1681
+ borderTopLeftRadius: "var(--radius)",
1682
+ borderTopRightRadius: "var(--radius)",
1683
+ borderTop: "1px solid var(--color-border, #e2e8f0)"
1684
+ },
1685
+ ".cm-draftly-table-line-end": {
1686
+ borderBottomLeftRadius: "var(--radius)",
1687
+ borderBottomRightRadius: "var(--radius)",
1688
+ borderBottom: "1px solid var(--color-border, #e2e8f0)"
1689
+ },
1690
+ ".cm-draftly-table-delimiter-line": {
1691
+ opacity: "0.5"
1692
+ },
1693
+ // Hidden table text (when cursor is not in range)
1694
+ ".cm-draftly-table-hidden": {
1695
+ display: "none"
1696
+ },
1697
+ // Line decoration for rendered state — hide line breaks
1698
+ ".cm-draftly-table-rendered": {
1699
+ padding: "0 !important"
1700
+ },
1701
+ ".cm-draftly-table-rendered br": {
1702
+ display: "none"
1703
+ },
1704
+ // Rendered table widget container
1705
+ ".cm-draftly-table-widget": {
1706
+ cursor: "pointer",
1707
+ overflow: "auto",
1708
+ padding: "0.5rem 0"
1709
+ },
1710
+ // Table element
1711
+ ".cm-draftly-table": {
1712
+ width: "100%",
1713
+ borderCollapse: "separate",
1714
+ borderSpacing: "0",
1715
+ borderRadius: "0.5rem",
1716
+ overflow: "hidden",
1717
+ border: "1px solid var(--color-border, #e2e8f0)",
1718
+ fontFamily: "var(--font-sans, sans-serif)",
1719
+ fontSize: "0.9375rem",
1720
+ lineHeight: "1.5"
1721
+ },
1722
+ // Table header
1723
+ ".cm-draftly-table thead th": {
1724
+ padding: "0rem 0.875rem",
1725
+ fontWeight: "600",
1726
+ borderBottom: "2px solid var(--color-border, #e2e8f0)",
1727
+ backgroundColor: "rgba(0, 0, 0, 0.03)"
1728
+ },
1729
+ // Table cells
1730
+ ".cm-draftly-table td": {
1731
+ padding: "0rem 0.875rem",
1732
+ borderBottom: "1px solid var(--color-border, #e2e8f0)",
1733
+ borderRight: "1px solid var(--color-border, #e2e8f0)"
1734
+ },
1735
+ // Remove right border on last cell
1736
+ ".cm-draftly-table td:last-child, .cm-draftly-table th:last-child": {
1737
+ borderRight: "none"
1738
+ },
1739
+ // Remove bottom border on last row
1740
+ ".cm-draftly-table tbody tr:last-child td": {
1741
+ borderBottom: "none"
1742
+ },
1743
+ // Alternate row colors
1744
+ ".cm-draftly-table tbody tr:nth-child(even)": {
1745
+ backgroundColor: "rgba(0, 0, 0, 0.02)"
1746
+ },
1747
+ // Header cells right border
1748
+ ".cm-draftly-table thead th:not(:last-child)": {
1749
+ borderRight: "1px solid var(--color-border, #e2e8f0)"
1750
+ },
1751
+ // Hover effect on rows
1752
+ ".cm-draftly-table tbody tr:hover": {
1753
+ backgroundColor: "rgba(0, 0, 0, 0.04)"
1754
+ },
1755
+ // Inline code in table cells
1756
+ ".cm-draftly-table-inline-code": {
1757
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
1758
+ fontSize: "0.85em",
1759
+ padding: "0.1em 0.35em",
1760
+ borderRadius: "0.25rem",
1761
+ backgroundColor: "rgba(0, 0, 0, 0.06)"
1762
+ },
1763
+ // Links in table cells
1764
+ ".cm-draftly-table-link": {
1765
+ color: "var(--color-link, #0969da)",
1766
+ textDecoration: "none"
1767
+ },
1768
+ ".cm-draftly-table-link:hover": {
1769
+ textDecoration: "underline"
1770
+ },
1771
+ // Math in table cells
1772
+ ".cm-draftly-table-math": {
1773
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
1774
+ fontSize: "0.9em",
1775
+ color: "#6a737d"
1776
+ }
1777
+ },
1778
+ dark: {
1779
+ ".cm-draftly-table-line": {
1780
+ backgroundColor: "rgba(255, 255, 255, 0.03)"
1781
+ },
1782
+ ".cm-draftly-table thead th": {
1783
+ backgroundColor: "rgba(255, 255, 255, 0.05)"
1784
+ },
1785
+ ".cm-draftly-table tbody tr:nth-child(even)": {
1786
+ backgroundColor: "rgba(255, 255, 255, 0.02)"
1787
+ },
1788
+ ".cm-draftly-table tbody tr:hover": {
1789
+ backgroundColor: "rgba(255, 255, 255, 0.05)"
1790
+ },
1791
+ ".cm-draftly-table-inline-code": {
1792
+ backgroundColor: "rgba(255, 255, 255, 0.08)"
1793
+ },
1794
+ ".cm-draftly-table-link": {
1795
+ color: "var(--color-link, #58a6ff)"
1796
+ },
1797
+ ".cm-draftly-table-math": {
1798
+ color: "#8b949e"
1799
+ }
1800
+ }
1801
+ });
1122
1802
  var htmlMarkDecorations = {
1123
1803
  "html-tag": view.Decoration.mark({ class: "cm-draftly-html-tag" }),
1124
1804
  "html-comment": view.Decoration.mark({ class: "cm-draftly-html-comment" })
@@ -1174,7 +1854,7 @@ function parseHTMLTag(content) {
1174
1854
  )
1175
1855
  };
1176
1856
  }
1177
- var HTMLPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
1857
+ var HTMLPlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
1178
1858
  name = "html";
1179
1859
  version = "1.0.0";
1180
1860
  decorationPriority = 30;
@@ -1185,7 +1865,7 @@ var HTMLPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
1185
1865
  * Plugin theme
1186
1866
  */
1187
1867
  get theme() {
1188
- return theme6;
1868
+ return theme7;
1189
1869
  }
1190
1870
  buildDecorations(ctx) {
1191
1871
  const { view: view$1, decorations } = ctx;
@@ -1325,7 +2005,7 @@ var HTMLPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
1325
2005
  }
1326
2006
  }
1327
2007
  };
1328
- var theme6 = chunkKDEDLC3D_cjs.createTheme({
2008
+ var theme7 = chunkTBVZEK2H_cjs.createTheme({
1329
2009
  default: {
1330
2010
  ".cm-draftly-html-tag": {
1331
2011
  color: "#6a737d",
@@ -1443,7 +2123,7 @@ var ImageWidget = class extends view.WidgetType {
1443
2123
  return event.type !== "click";
1444
2124
  }
1445
2125
  };
1446
- var ImagePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2126
+ var ImagePlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
1447
2127
  name = "image";
1448
2128
  version = "1.0.0";
1449
2129
  decorationPriority = 25;
@@ -1455,7 +2135,7 @@ var ImagePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
1455
2135
  * Plugin theme
1456
2136
  */
1457
2137
  get theme() {
1458
- return theme7;
2138
+ return theme8;
1459
2139
  }
1460
2140
  /**
1461
2141
  * Keyboard shortcuts for image formatting
@@ -1610,7 +2290,7 @@ var ImagePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
1610
2290
  return html;
1611
2291
  }
1612
2292
  };
1613
- var theme7 = chunkKDEDLC3D_cjs.createTheme({
2293
+ var theme8 = chunkTBVZEK2H_cjs.createTheme({
1614
2294
  default: {
1615
2295
  ".cm-draftly-image-block br": {
1616
2296
  display: "none"
@@ -1847,7 +2527,7 @@ var mathBlockParser = {
1847
2527
  return true;
1848
2528
  }
1849
2529
  };
1850
- var MathPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2530
+ var MathPlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
1851
2531
  name = "math";
1852
2532
  version = "1.0.0";
1853
2533
  decorationPriority = 25;
@@ -1859,7 +2539,16 @@ var MathPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
1859
2539
  * Plugin theme
1860
2540
  */
1861
2541
  get theme() {
1862
- return theme8;
2542
+ return theme9;
2543
+ }
2544
+ /**
2545
+ * Intercepts dollar typing to wrap selected text as inline math.
2546
+ *
2547
+ * If user types '$' while text is selected, wraps each selected range
2548
+ * with single dollars (selected -> $selected$).
2549
+ */
2550
+ getExtensions() {
2551
+ return [chunk3T55CBNZ_cjs.createWrapSelectionInputHandler({ "$": "$" })];
1863
2552
  }
1864
2553
  /**
1865
2554
  * Return markdown parser extensions for math syntax
@@ -1967,7 +2656,7 @@ var MathPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
1967
2656
  return null;
1968
2657
  }
1969
2658
  };
1970
- var theme8 = chunkKDEDLC3D_cjs.createTheme({
2659
+ var theme9 = chunkTBVZEK2H_cjs.createTheme({
1971
2660
  default: {
1972
2661
  ".cm-draftly-math-block": {
1973
2662
  fontFamily: "var(--font-jetbrains-mono, monospace)"
@@ -2154,7 +2843,7 @@ var mermaidBlockParser = {
2154
2843
  return true;
2155
2844
  }
2156
2845
  };
2157
- var MermaidPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2846
+ var MermaidPlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
2158
2847
  name = "mermaid";
2159
2848
  version = "1.0.0";
2160
2849
  decorationPriority = 25;
@@ -2166,7 +2855,7 @@ var MermaidPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2166
2855
  * Plugin theme
2167
2856
  */
2168
2857
  get theme() {
2169
- return theme9;
2858
+ return theme10;
2170
2859
  }
2171
2860
  /**
2172
2861
  * Return markdown parser extensions for mermaid syntax
@@ -2274,7 +2963,7 @@ var MermaidPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2274
2963
  return null;
2275
2964
  }
2276
2965
  };
2277
- var theme9 = chunkKDEDLC3D_cjs.createTheme({
2966
+ var theme10 = chunkTBVZEK2H_cjs.createTheme({
2278
2967
  default: {
2279
2968
  // Raw mermaid block lines (monospace)
2280
2969
  ".cm-draftly-mermaid-block:not(.cm-draftly-mermaid-block-rendered)": {
@@ -2378,22 +3067,395 @@ var theme9 = chunkKDEDLC3D_cjs.createTheme({
2378
3067
  }
2379
3068
  }
2380
3069
  });
2381
- var COPY_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
2382
- var CHECK_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`;
2383
- var COPY_RESET_DELAY = 2e3;
2384
- var codeMarkDecorations = {
2385
- // Inline code
2386
- "inline-code": view.Decoration.mark({ class: "cm-draftly-code-inline" }),
2387
- "inline-mark": view.Decoration.replace({}),
2388
- // Fenced code block
2389
- "code-block-line": view.Decoration.line({ class: "cm-draftly-code-block-line" }),
2390
- "code-block-line-start": view.Decoration.line({ class: "cm-draftly-code-block-line-start" }),
2391
- "code-block-line-end": view.Decoration.line({ class: "cm-draftly-code-block-line-end" }),
2392
- "code-fence": view.Decoration.mark({ class: "cm-draftly-code-fence" }),
2393
- "code-hidden": view.Decoration.replace({}),
3070
+
3071
+ // src/plugins/code-plugin.theme.ts
3072
+ var codePluginTheme = chunkTBVZEK2H_cjs.createTheme({
3073
+ default: {
3074
+ // Inline code
3075
+ ".cm-draftly-code-inline": {
3076
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
3077
+ fontSize: "0.9rem",
3078
+ backgroundColor: "rgba(0, 0, 0, 0.05)",
3079
+ padding: "0.1rem 0.25rem",
3080
+ border: "1px solid var(--color-border)",
3081
+ borderRadius: "3px"
3082
+ },
3083
+ // Fenced code block lines
3084
+ ".cm-draftly-code-block-line": {
3085
+ "--radius": "0.375rem",
3086
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
3087
+ fontSize: "0.9rem",
3088
+ backgroundColor: "rgba(0, 0, 0, 0.03)",
3089
+ padding: "0 1rem !important",
3090
+ lineHeight: "1.5",
3091
+ borderLeft: "1px solid var(--color-border)",
3092
+ borderRight: "1px solid var(--color-border)"
3093
+ },
3094
+ // First line of code block
3095
+ ".cm-draftly-code-block-line-start": {
3096
+ borderTopLeftRadius: "var(--radius)",
3097
+ borderTopRightRadius: "var(--radius)",
3098
+ position: "relative",
3099
+ overflow: "hidden",
3100
+ borderTop: "1px solid var(--color-border)",
3101
+ paddingBottom: "0.5rem !important"
3102
+ },
3103
+ // Remove top radius when header is present
3104
+ ".cm-draftly-code-block-has-header": {
3105
+ padding: "0 !important",
3106
+ paddingBottom: "0.5rem !important"
3107
+ },
3108
+ // Code block header widget
3109
+ ".cm-draftly-code-header": {
3110
+ display: "flex",
3111
+ justifyContent: "space-between",
3112
+ alignItems: "center",
3113
+ padding: "0.25rem 1rem",
3114
+ backgroundColor: "rgba(0, 0, 0, 0.06)",
3115
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
3116
+ fontSize: "0.85rem",
3117
+ ".cm-draftly-code-header-left": {
3118
+ display: "flex",
3119
+ alignItems: "center",
3120
+ gap: "0.5rem",
3121
+ ".cm-draftly-code-header-title": {
3122
+ color: "var(--color-text, inherit)",
3123
+ fontWeight: "500"
3124
+ },
3125
+ ".cm-draftly-code-header-lang": {
3126
+ color: "#6a737d",
3127
+ opacity: "0.8"
3128
+ }
3129
+ },
3130
+ ".cm-draftly-code-header-right": {
3131
+ display: "flex",
3132
+ alignItems: "center",
3133
+ gap: "0.5rem",
3134
+ ".cm-draftly-code-copy-btn": {
3135
+ display: "flex",
3136
+ alignItems: "center",
3137
+ justifyContent: "center",
3138
+ padding: "0.25rem",
3139
+ backgroundColor: "transparent",
3140
+ border: "none",
3141
+ borderRadius: "4px",
3142
+ cursor: "pointer",
3143
+ color: "#6a737d",
3144
+ transition: "color 0.2s, background-color 0.2s",
3145
+ "&:hover": {
3146
+ backgroundColor: "rgba(0, 0, 0, 0.1)",
3147
+ color: "var(--color-text, inherit)"
3148
+ },
3149
+ "&.copied": {
3150
+ color: "#22c55e"
3151
+ }
3152
+ }
3153
+ }
3154
+ },
3155
+ // Caption (below code block)
3156
+ ".cm-draftly-code-block-has-caption": {
3157
+ padding: "0 !important",
3158
+ paddingTop: "0.5rem !important"
3159
+ },
3160
+ ".cm-draftly-code-caption": {
3161
+ textAlign: "center",
3162
+ fontSize: "0.85rem",
3163
+ color: "#6a737d",
3164
+ fontStyle: "italic",
3165
+ padding: "0.25rem 1rem",
3166
+ backgroundColor: "rgba(0, 0, 0, 0.06)"
3167
+ },
3168
+ // Last line of code block
3169
+ ".cm-draftly-code-block-line-end": {
3170
+ borderBottomLeftRadius: "var(--radius)",
3171
+ borderBottomRightRadius: "var(--radius)",
3172
+ borderBottom: "1px solid var(--color-border)",
3173
+ paddingTop: "0.5rem !important",
3174
+ "& br": {
3175
+ display: "none"
3176
+ }
3177
+ },
3178
+ // Fence markers (```)
3179
+ ".cm-draftly-code-fence": {
3180
+ color: "#6a737d",
3181
+ fontFamily: "var(--font-jetbrains-mono, monospace)"
3182
+ },
3183
+ // Line numbers
3184
+ ".cm-draftly-code-line-numbered": {
3185
+ paddingLeft: "calc(var(--line-num-width, 2ch) + 1rem) !important",
3186
+ position: "relative",
3187
+ "&::before": {
3188
+ content: "attr(data-line-num)",
3189
+ position: "absolute",
3190
+ left: "0.5rem",
3191
+ top: "0.2rem",
3192
+ width: "var(--line-num-width, 2ch)",
3193
+ textAlign: "right",
3194
+ color: "#6a737d",
3195
+ opacity: "0.6",
3196
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
3197
+ fontSize: "0.85rem",
3198
+ userSelect: "none"
3199
+ }
3200
+ },
3201
+ ".cm-draftly-code-line-numbered-diff": {
3202
+ paddingLeft: "calc(var(--line-num-old-width, 2ch) + var(--line-num-new-width, 2ch) + 2.75rem) !important",
3203
+ position: "relative",
3204
+ "&::before": {
3205
+ content: "attr(data-line-num-old)",
3206
+ position: "absolute",
3207
+ left: "0.5rem",
3208
+ top: "0.2rem",
3209
+ width: "var(--line-num-old-width, 2ch)",
3210
+ textAlign: "right",
3211
+ color: "#6a737d",
3212
+ opacity: "0.6",
3213
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
3214
+ fontSize: "0.85rem",
3215
+ userSelect: "none"
3216
+ },
3217
+ "&::after": {
3218
+ content: 'attr(data-line-num-new) " " attr(data-diff-marker)',
3219
+ position: "absolute",
3220
+ left: "calc(0.5rem + var(--line-num-old-width, 2ch) + 0.75rem)",
3221
+ top: "0.2rem",
3222
+ width: "calc(var(--line-num-new-width, 2ch) + 2ch)",
3223
+ textAlign: "right",
3224
+ color: "#6a737d",
3225
+ opacity: "0.6",
3226
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
3227
+ fontSize: "0.85rem",
3228
+ userSelect: "none"
3229
+ },
3230
+ "&.cm-draftly-code-line-diff-gutter": {
3231
+ paddingLeft: "calc(var(--line-num-width, 2ch) + 2rem) !important",
3232
+ "&::after": {
3233
+ content: "attr(data-diff-marker)",
3234
+ position: "absolute",
3235
+ left: "calc(0.5rem + var(--line-num-width, 2ch) + 0.35rem)",
3236
+ top: "0.1rem",
3237
+ width: "1ch",
3238
+ textAlign: "right",
3239
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
3240
+ fontSize: "0.85rem",
3241
+ fontWeight: "700",
3242
+ userSelect: "none"
3243
+ }
3244
+ }
3245
+ },
3246
+ // Preview: code lines (need block display for full-width highlights)
3247
+ ".cm-draftly-code-line": {
3248
+ display: "block",
3249
+ position: "relative",
3250
+ paddingLeft: "1rem",
3251
+ paddingRight: "1rem",
3252
+ lineHeight: "1.5",
3253
+ borderLeft: "3px solid transparent"
3254
+ },
3255
+ // Line highlight
3256
+ ".cm-draftly-code-line-highlight": {
3257
+ backgroundColor: "rgba(255, 220, 100, 0.2) !important",
3258
+ borderLeft: "3px solid #f0b429 !important"
3259
+ },
3260
+ ".cm-draftly-code-line-diff-add": {
3261
+ color: "inherit",
3262
+ backgroundColor: "rgba(34, 197, 94, 0.12) !important",
3263
+ borderLeft: "3px solid #22c55e !important",
3264
+ "&.cm-draftly-code-line-diff-gutter::after": {
3265
+ color: "#16a34a"
3266
+ }
3267
+ },
3268
+ ".cm-draftly-code-line-diff-del": {
3269
+ color: "inherit",
3270
+ backgroundColor: "rgba(239, 68, 68, 0.12) !important",
3271
+ borderLeft: "3px solid #ef4444 !important",
3272
+ "&.cm-draftly-code-line-diff-gutter::after": {
3273
+ color: "#dc2626"
3274
+ }
3275
+ },
3276
+ ".cm-draftly-code-diff-sign-add": {
3277
+ color: "#16a34a",
3278
+ fontWeight: "700"
3279
+ },
3280
+ ".cm-draftly-code-diff-sign-del": {
3281
+ color: "#dc2626",
3282
+ fontWeight: "700"
3283
+ },
3284
+ ".cm-draftly-code-diff-mod-add": {
3285
+ color: "inherit",
3286
+ backgroundColor: "rgba(34, 197, 94, 0.25)",
3287
+ borderRadius: "2px",
3288
+ padding: "0.1rem 0"
3289
+ },
3290
+ ".cm-draftly-code-diff-mod-del": {
3291
+ color: "inherit",
3292
+ backgroundColor: "rgba(239, 68, 68, 0.25)",
3293
+ borderRadius: "2px",
3294
+ padding: "0.1rem 0"
3295
+ },
3296
+ // Text highlight
3297
+ ".cm-draftly-code-text-highlight": {
3298
+ color: "inherit",
3299
+ backgroundColor: "rgba(255, 220, 100, 0.4)",
3300
+ borderRadius: "2px",
3301
+ padding: "0.1rem 0"
3302
+ },
3303
+ // Preview: container wrapper
3304
+ ".cm-draftly-code-container": {
3305
+ margin: "1rem 0",
3306
+ borderRadius: "var(--radius)",
3307
+ overflow: "hidden",
3308
+ border: "1px solid var(--color-border)",
3309
+ ".cm-draftly-code-header": {
3310
+ borderRadius: "0",
3311
+ border: "none",
3312
+ borderBottom: "1px solid var(--color-border)"
3313
+ },
3314
+ ".cm-draftly-code-block": {
3315
+ margin: "0",
3316
+ borderRadius: "0",
3317
+ border: "none",
3318
+ whiteSpace: "pre-wrap"
3319
+ },
3320
+ ".cm-draftly-code-caption": {
3321
+ borderTop: "1px solid var(--color-border)"
3322
+ }
3323
+ },
3324
+ // Preview: standalone code block (not in container)
3325
+ ".cm-draftly-code-block": {
3326
+ fontFamily: "var(--font-jetbrains-mono, monospace)",
3327
+ fontSize: "0.9rem",
3328
+ backgroundColor: "rgba(0, 0, 0, 0.03)",
3329
+ padding: "1rem",
3330
+ overflow: "auto",
3331
+ position: "relative",
3332
+ borderRadius: "var(--radius)",
3333
+ border: "1px solid var(--color-border)",
3334
+ "&.cm-draftly-code-block-has-header": {
3335
+ borderTopLeftRadius: "0",
3336
+ borderTopRightRadius: "0",
3337
+ borderTop: "none",
3338
+ margin: "0",
3339
+ paddingTop: "0.5rem !important"
3340
+ },
3341
+ "&.cm-draftly-code-block-has-caption": {
3342
+ borderBottomLeftRadius: "0",
3343
+ borderBottomRightRadius: "0",
3344
+ borderBottom: "none",
3345
+ paddingBottom: "0.5rem !important"
3346
+ }
3347
+ }
3348
+ },
3349
+ dark: {
3350
+ ".cm-draftly-code-inline": {
3351
+ backgroundColor: "rgba(255, 255, 255, 0.1)"
3352
+ },
3353
+ ".cm-draftly-code-block-line": {
3354
+ backgroundColor: "rgba(255, 255, 255, 0.05)"
3355
+ },
3356
+ ".cm-draftly-code-fence": {
3357
+ color: "#8b949e"
3358
+ },
3359
+ ".cm-draftly-code-block": {
3360
+ backgroundColor: "rgba(255, 255, 255, 0.05)"
3361
+ },
3362
+ ".cm-draftly-code-header": {
3363
+ backgroundColor: "rgba(255, 255, 255, 0.08)",
3364
+ ".cm-draftly-code-header-lang": {
3365
+ color: "#8b949e"
3366
+ },
3367
+ ".cm-draftly-code-copy-btn": {
3368
+ color: "#8b949e",
3369
+ "&:hover": {
3370
+ backgroundColor: "rgba(255, 255, 255, 0.1)"
3371
+ }
3372
+ }
3373
+ },
3374
+ ".cm-draftly-code-caption": {
3375
+ backgroundColor: "rgba(255, 255, 255, 0.05)"
3376
+ },
3377
+ ".cm-draftly-code-line-numbered": {
3378
+ "&::before": {
3379
+ color: "#8b949e"
3380
+ }
3381
+ },
3382
+ ".cm-draftly-code-line-numbered-diff": {
3383
+ "&::before": {
3384
+ color: "#8b949e"
3385
+ },
3386
+ "&::after": {
3387
+ color: "#8b949e"
3388
+ }
3389
+ },
3390
+ ".cm-draftly-code-line-diff-gutter": {
3391
+ "&::after": {
3392
+ color: "#8b949e"
3393
+ }
3394
+ },
3395
+ ".cm-draftly-code-line-highlight": {
3396
+ backgroundColor: "rgba(255, 220, 100, 0.15) !important",
3397
+ borderLeft: "3px solid #d9a520 !important"
3398
+ },
3399
+ ".cm-draftly-code-line-diff-add": {
3400
+ backgroundColor: "rgba(34, 197, 94, 0.15) !important",
3401
+ borderLeft: "3px solid #22c55e !important",
3402
+ "&.cm-draftly-code-line-diff-gutter::after": {
3403
+ color: "#4ade80"
3404
+ }
3405
+ },
3406
+ ".cm-draftly-code-line-diff-del": {
3407
+ backgroundColor: "rgba(239, 68, 68, 0.15) !important",
3408
+ borderLeft: "3px solid #ef4444 !important",
3409
+ "&.cm-draftly-code-line-diff-gutter::after": {
3410
+ color: "#f87171"
3411
+ }
3412
+ },
3413
+ ".cm-draftly-code-diff-sign-add": {
3414
+ color: "#4ade80"
3415
+ },
3416
+ ".cm-draftly-code-diff-sign-del": {
3417
+ color: "#f87171"
3418
+ },
3419
+ ".cm-draftly-code-diff-mod-add": {
3420
+ backgroundColor: "rgba(34, 197, 94, 0.3)"
3421
+ },
3422
+ ".cm-draftly-code-diff-mod-del": {
3423
+ backgroundColor: "rgba(239, 68, 68, 0.3)"
3424
+ },
3425
+ ".cm-draftly-code-text-highlight": {
3426
+ backgroundColor: "rgba(255, 220, 100, 0.3)"
3427
+ }
3428
+ }
3429
+ });
3430
+
3431
+ // src/plugins/code-plugin.ts
3432
+ var COPY_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
3433
+ var CHECK_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`;
3434
+ var COPY_RESET_DELAY = 2e3;
3435
+ var CODE_FENCE = "```";
3436
+ var QUOTED_INFO_PATTERN = /(\w+)="([^"]*)"/g;
3437
+ var TEXT_HIGHLIGHT_PATTERN = /\/([^/]+)\/(?:(\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*))?/g;
3438
+ var codeMarkDecorations = {
3439
+ // Inline code
3440
+ "inline-code": view.Decoration.mark({ class: "cm-draftly-code-inline" }),
3441
+ "inline-mark": view.Decoration.replace({}),
3442
+ // Fenced code block
3443
+ "code-block-line": view.Decoration.line({ class: "cm-draftly-code-block-line" }),
3444
+ "code-block-line-start": view.Decoration.line({ class: "cm-draftly-code-block-line-start" }),
3445
+ "code-block-line-end": view.Decoration.line({ class: "cm-draftly-code-block-line-end" }),
3446
+ "code-fence": view.Decoration.mark({ class: "cm-draftly-code-fence" }),
3447
+ "code-hidden": view.Decoration.replace({}),
2394
3448
  // Highlights
2395
3449
  "code-line-highlight": view.Decoration.line({ class: "cm-draftly-code-line-highlight" }),
2396
- "code-text-highlight": view.Decoration.mark({ class: "cm-draftly-code-text-highlight" })
3450
+ "code-text-highlight": view.Decoration.mark({ class: "cm-draftly-code-text-highlight" }),
3451
+ // Diff preview
3452
+ "diff-line-add": view.Decoration.line({ class: "cm-draftly-code-line-diff-add" }),
3453
+ "diff-line-del": view.Decoration.line({ class: "cm-draftly-code-line-diff-del" }),
3454
+ "diff-sign-add": view.Decoration.mark({ class: "cm-draftly-code-diff-sign-add" }),
3455
+ "diff-sign-del": view.Decoration.mark({ class: "cm-draftly-code-diff-sign-del" }),
3456
+ "diff-mod-add": view.Decoration.mark({ class: "cm-draftly-code-diff-mod-add" }),
3457
+ "diff-mod-del": view.Decoration.mark({ class: "cm-draftly-code-diff-mod-del" }),
3458
+ "diff-escape-hidden": view.Decoration.replace({})
2397
3459
  };
2398
3460
  var CodeBlockHeaderWidget = class extends view.WidgetType {
2399
3461
  constructor(props, codeContent) {
@@ -2474,16 +3536,17 @@ var CodeBlockCaptionWidget = class extends view.WidgetType {
2474
3536
  return false;
2475
3537
  }
2476
3538
  };
2477
- var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
3539
+ var CodePlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
2478
3540
  name = "code";
2479
3541
  version = "1.0.0";
2480
3542
  decorationPriority = 25;
2481
3543
  requiredNodes = ["InlineCode", "FencedCode", "CodeMark", "CodeInfo", "CodeText"];
3544
+ parserCache = /* @__PURE__ */ new Map();
2482
3545
  /**
2483
3546
  * Plugin theme
2484
3547
  */
2485
3548
  get theme() {
2486
- return theme10;
3549
+ return codePluginTheme;
2487
3550
  }
2488
3551
  /**
2489
3552
  * Keyboard shortcuts for code formatting
@@ -2492,7 +3555,7 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2492
3555
  return [
2493
3556
  {
2494
3557
  key: "Mod-e",
2495
- run: chunkKDEDLC3D_cjs.toggleMarkdownStyle("`"),
3558
+ run: chunkTBVZEK2H_cjs.toggleMarkdownStyle("`"),
2496
3559
  preventDefault: true
2497
3560
  },
2498
3561
  {
@@ -2502,6 +3565,15 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2502
3565
  }
2503
3566
  ];
2504
3567
  }
3568
+ /**
3569
+ * Intercepts backtick typing to wrap selected text as inline code.
3570
+ *
3571
+ * If user types '`' while text is selected, wraps each selected range
3572
+ * with backticks (selected -> `selected`).
3573
+ */
3574
+ getExtensions() {
3575
+ return [chunk3T55CBNZ_cjs.createWrapSelectionInputHandler({ "`": "`" })];
3576
+ }
2505
3577
  /**
2506
3578
  * Toggle code block on current line or selected lines
2507
3579
  */
@@ -2514,7 +3586,7 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2514
3586
  const nextLineNum = endLine.number < state.doc.lines ? endLine.number + 1 : endLine.number;
2515
3587
  const prevLine = state.doc.line(prevLineNum);
2516
3588
  const nextLine = state.doc.line(nextLineNum);
2517
- const isWrapped = prevLine.text.trim().startsWith("```") && nextLine.text.trim() === "```" && prevLineNum !== startLine.number && nextLineNum !== endLine.number;
3589
+ const isWrapped = prevLine.text.trim().startsWith(CODE_FENCE) && nextLine.text.trim() === CODE_FENCE && prevLineNum !== startLine.number && nextLineNum !== endLine.number;
2518
3590
  if (isWrapped) {
2519
3591
  view.dispatch({
2520
3592
  changes: [
@@ -2525,8 +3597,10 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2525
3597
  ]
2526
3598
  });
2527
3599
  } else {
2528
- const openFence = "```\n";
2529
- const closeFence = "\n```";
3600
+ const openFence = `${CODE_FENCE}
3601
+ `;
3602
+ const closeFence = `
3603
+ ${CODE_FENCE}`;
2530
3604
  view.dispatch({
2531
3605
  changes: [
2532
3606
  { from: startLine.from, insert: openFence },
@@ -2555,6 +3629,7 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2555
3629
  * lineNumbers: 5,
2556
3630
  * title: "hello.tsx",
2557
3631
  * copy: true,
3632
+ * diff: false,
2558
3633
  * highlightLines: [2,3,4,5],
2559
3634
  * highlightText: [{ pattern: "Hello", instances: [3,4,5] }]
2560
3635
  * }
@@ -2566,14 +3641,21 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2566
3641
  return props;
2567
3642
  }
2568
3643
  let remaining = codeInfo.trim();
2569
- const langMatch = remaining.match(/^(\w+)/);
2570
- if (langMatch && langMatch[1]) {
2571
- props.language = langMatch[1];
2572
- remaining = remaining.slice(langMatch[0].length).trim();
3644
+ const firstTokenMatch = remaining.match(/^([^\s]+)/);
3645
+ if (firstTokenMatch && firstTokenMatch[1]) {
3646
+ const firstToken = firstTokenMatch[1];
3647
+ const normalizedToken = firstToken.toLowerCase();
3648
+ const isLineNumberDirective = /^(?:line-numbers|linenumbers|showlinenumbers)(?:\{\d+\})?$/.test(
3649
+ normalizedToken
3650
+ );
3651
+ const isKnownDirective = isLineNumberDirective || normalizedToken === "copy" || normalizedToken === "diff" || normalizedToken.startsWith("{") || normalizedToken.startsWith("/");
3652
+ if (!isKnownDirective) {
3653
+ props.language = firstToken;
3654
+ remaining = remaining.slice(firstToken.length).trim();
3655
+ }
2573
3656
  }
2574
- const quotedPattern = /(\w+)="([^"]*)"/g;
2575
3657
  let quotedMatch;
2576
- while ((quotedMatch = quotedPattern.exec(remaining)) !== null) {
3658
+ while ((quotedMatch = QUOTED_INFO_PATTERN.exec(remaining)) !== null) {
2577
3659
  const key = quotedMatch[1]?.toLowerCase();
2578
3660
  const value = quotedMatch[2];
2579
3661
  if (key === "title" && value !== void 0) {
@@ -2582,13 +3664,13 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2582
3664
  props.caption = value;
2583
3665
  }
2584
3666
  }
2585
- remaining = remaining.replace(quotedPattern, "").trim();
2586
- const lineNumbersMatch = remaining.match(/line-numbers(?:\{(\d+)\})?/);
3667
+ remaining = remaining.replace(QUOTED_INFO_PATTERN, "").trim();
3668
+ const lineNumbersMatch = remaining.match(/\b(?:line-numbers|lineNumbers|showLineNumbers)(?:\{(\d+)\})?/i);
2587
3669
  if (lineNumbersMatch) {
2588
3670
  if (lineNumbersMatch[1]) {
2589
- props.lineNumbers = parseInt(lineNumbersMatch[1], 10);
3671
+ props.showLineNumbers = parseInt(lineNumbersMatch[1], 10);
2590
3672
  } else {
2591
- props.lineNumbers = true;
3673
+ props.showLineNumbers = true;
2592
3674
  }
2593
3675
  remaining = remaining.replace(lineNumbersMatch[0], "").trim();
2594
3676
  }
@@ -2596,52 +3678,27 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2596
3678
  props.copy = true;
2597
3679
  remaining = remaining.replace(/\bcopy\b/, "").trim();
2598
3680
  }
3681
+ if (/\bdiff\b/.test(remaining)) {
3682
+ props.diff = true;
3683
+ remaining = remaining.replace(/\bdiff\b/, "").trim();
3684
+ }
2599
3685
  const lineHighlightMatch = remaining.match(/\{([^}]+)\}/);
2600
3686
  if (lineHighlightMatch && lineHighlightMatch[1]) {
2601
- const highlightLines = [];
2602
- const parts = lineHighlightMatch[1].split(",");
2603
- for (const part of parts) {
2604
- const trimmed = part.trim();
2605
- const rangeMatch = trimmed.match(/^(\d+)-(\d+)$/);
2606
- if (rangeMatch && rangeMatch[1] && rangeMatch[2]) {
2607
- const start = parseInt(rangeMatch[1], 10);
2608
- const end = parseInt(rangeMatch[2], 10);
2609
- for (let i = start; i <= end; i++) {
2610
- highlightLines.push(i);
2611
- }
2612
- } else if (/^\d+$/.test(trimmed)) {
2613
- highlightLines.push(parseInt(trimmed, 10));
2614
- }
2615
- }
3687
+ const highlightLines = this.parseNumberList(lineHighlightMatch[1]);
2616
3688
  if (highlightLines.length > 0) {
2617
3689
  props.highlightLines = highlightLines;
2618
3690
  }
2619
3691
  remaining = remaining.replace(lineHighlightMatch[0], "").trim();
2620
3692
  }
2621
- const textHighlightPattern = /\/([^/]+)\/(?:(\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*))?/g;
2622
3693
  let textMatch;
2623
3694
  const highlightText = [];
2624
- while ((textMatch = textHighlightPattern.exec(remaining)) !== null) {
3695
+ while ((textMatch = TEXT_HIGHLIGHT_PATTERN.exec(remaining)) !== null) {
2625
3696
  if (!textMatch[1]) continue;
2626
3697
  const highlight = {
2627
3698
  pattern: textMatch[1]
2628
3699
  };
2629
3700
  if (textMatch[2]) {
2630
- const instanceStr = textMatch[2];
2631
- const instances = [];
2632
- const instanceParts = instanceStr.split(",");
2633
- for (const part of instanceParts) {
2634
- const rangeMatch = part.match(/^(\d+)-(\d+)$/);
2635
- if (rangeMatch && rangeMatch[1] && rangeMatch[2]) {
2636
- const start = parseInt(rangeMatch[1], 10);
2637
- const end = parseInt(rangeMatch[2], 10);
2638
- for (let i = start; i <= end; i++) {
2639
- instances.push(i);
2640
- }
2641
- } else if (/^\d+$/.test(part)) {
2642
- instances.push(parseInt(part, 10));
2643
- }
2644
- }
3701
+ const instances = this.parseNumberList(textMatch[2]);
2645
3702
  if (instances.length > 0) {
2646
3703
  highlight.instances = instances;
2647
3704
  }
@@ -2658,148 +3715,244 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2658
3715
  * Handles line numbers, highlights, header/caption widgets, and fence visibility.
2659
3716
  */
2660
3717
  buildDecorations(ctx) {
2661
- const { view: view$1, decorations } = ctx;
2662
- const tree = language.syntaxTree(view$1.state);
3718
+ const tree = language.syntaxTree(ctx.view.state);
2663
3719
  tree.iterate({
2664
3720
  enter: (node) => {
2665
- const { from, to, name } = node;
2666
- if (name === "InlineCode") {
2667
- decorations.push(codeMarkDecorations["inline-code"].range(from, to));
2668
- const cursorInRange = ctx.selectionOverlapsRange(from, to);
2669
- if (!cursorInRange) {
2670
- for (let child = node.node.firstChild; child; child = child.nextSibling) {
2671
- if (child.name === "CodeMark") {
2672
- decorations.push(codeMarkDecorations["inline-mark"].range(child.from, child.to));
2673
- }
2674
- }
2675
- }
3721
+ if (node.name === "InlineCode") {
3722
+ this.decorateInlineCode(node, ctx);
3723
+ return;
2676
3724
  }
2677
- if (name === "FencedCode") {
2678
- const nodeLineStart = view$1.state.doc.lineAt(from);
2679
- const nodeLineEnd = view$1.state.doc.lineAt(to);
2680
- const cursorInRange = ctx.selectionOverlapsRange(nodeLineStart.from, nodeLineEnd.to);
2681
- let infoProps = { language: "" };
2682
- for (let child = node.node.firstChild; child; child = child.nextSibling) {
2683
- if (child.name === "CodeInfo") {
2684
- infoProps = this.parseCodeInfo(view$1.state.sliceDoc(child.from, child.to).trim());
2685
- break;
2686
- }
2687
- }
2688
- const totalCodeLines = nodeLineEnd.number - nodeLineStart.number - 1;
2689
- const startLineNum = typeof infoProps.lineNumbers === "number" ? infoProps.lineNumbers : 1;
2690
- const maxLineNum = startLineNum + totalCodeLines - 1;
2691
- const lineNumWidth = Math.max(String(maxLineNum).length, String(startLineNum).length);
2692
- let codeLineIndex = 0;
2693
- let codeContent = "";
2694
- for (let child = node.node.firstChild; child; child = child.nextSibling) {
2695
- if (child.name === "CodeText") {
2696
- codeContent = view$1.state.sliceDoc(child.from, child.to);
2697
- break;
2698
- }
2699
- }
2700
- const shouldShowHeader = !cursorInRange && (infoProps.title || infoProps.copy || infoProps.language);
2701
- const shouldShowCaption = !cursorInRange && infoProps.caption;
2702
- if (shouldShowHeader) {
2703
- decorations.push(
2704
- view.Decoration.widget({
2705
- widget: new CodeBlockHeaderWidget(infoProps, codeContent),
2706
- block: false
2707
- }).range(nodeLineStart.from)
2708
- );
2709
- }
2710
- for (let i = nodeLineStart.number; i <= nodeLineEnd.number; i++) {
2711
- const line = view$1.state.doc.line(i);
2712
- const isFenceLine = i === nodeLineStart.number || i === nodeLineEnd.number;
2713
- const relativeLineNum = startLineNum + codeLineIndex;
2714
- decorations.push(codeMarkDecorations["code-block-line"].range(line.from));
2715
- if (i === nodeLineStart.number) {
2716
- decorations.push(codeMarkDecorations["code-block-line-start"].range(line.from));
2717
- if (shouldShowHeader) {
2718
- decorations.push(
2719
- view.Decoration.line({
2720
- class: "cm-draftly-code-block-has-header"
2721
- }).range(line.from)
2722
- );
2723
- }
2724
- }
2725
- if (i === nodeLineEnd.number) {
2726
- decorations.push(codeMarkDecorations["code-block-line-end"].range(line.from));
2727
- if (shouldShowCaption) {
2728
- decorations.push(
2729
- view.Decoration.line({
2730
- class: "cm-draftly-code-block-has-caption"
2731
- }).range(line.from)
2732
- );
2733
- }
2734
- }
2735
- if (!isFenceLine && infoProps.lineNumbers) {
2736
- decorations.push(
2737
- view.Decoration.line({
2738
- class: "cm-draftly-code-line-numbered",
2739
- attributes: {
2740
- "data-line-num": String(relativeLineNum),
2741
- style: `--line-num-width: ${lineNumWidth}ch`
2742
- }
2743
- }).range(line.from)
2744
- );
2745
- }
2746
- if (!isFenceLine && infoProps.highlightLines) {
2747
- if (infoProps.highlightLines.includes(codeLineIndex + 1)) {
2748
- decorations.push(codeMarkDecorations["code-line-highlight"].range(line.from));
2749
- }
2750
- }
2751
- if (!isFenceLine && infoProps.highlightText && infoProps.highlightText.length > 0) {
2752
- const lineText = view$1.state.sliceDoc(line.from, line.to);
2753
- for (const textHighlight of infoProps.highlightText) {
2754
- try {
2755
- const regex = new RegExp(textHighlight.pattern, "g");
2756
- let match;
2757
- let matchIndex = 0;
2758
- while ((match = regex.exec(lineText)) !== null) {
2759
- matchIndex++;
2760
- const shouldHighlight = !textHighlight.instances || textHighlight.instances.includes(matchIndex);
2761
- if (shouldHighlight) {
2762
- const matchFrom = line.from + match.index;
2763
- const matchTo = matchFrom + match[0].length;
2764
- decorations.push(codeMarkDecorations["code-text-highlight"].range(matchFrom, matchTo));
2765
- }
2766
- }
2767
- } catch {
2768
- }
2769
- }
2770
- }
2771
- if (!isFenceLine) {
2772
- codeLineIndex++;
3725
+ if (node.name === "FencedCode") {
3726
+ this.decorateFencedCode(node, ctx);
3727
+ }
3728
+ }
3729
+ });
3730
+ }
3731
+ decorateInlineCode(node, ctx) {
3732
+ const { from, to } = node;
3733
+ ctx.decorations.push(codeMarkDecorations["inline-code"].range(from, to));
3734
+ if (ctx.selectionOverlapsRange(from, to)) {
3735
+ return;
3736
+ }
3737
+ for (let child = node.node.firstChild; child; child = child.nextSibling) {
3738
+ if (child.name === "CodeMark") {
3739
+ ctx.decorations.push(codeMarkDecorations["inline-mark"].range(child.from, child.to));
3740
+ }
3741
+ }
3742
+ }
3743
+ decorateFencedCode(node, ctx) {
3744
+ const { view: view$1, decorations } = ctx;
3745
+ const nodeLineStart = view$1.state.doc.lineAt(node.from);
3746
+ const nodeLineEnd = view$1.state.doc.lineAt(node.to);
3747
+ const cursorInRange = ctx.selectionOverlapsRange(nodeLineStart.from, nodeLineEnd.to);
3748
+ let infoProps = { language: "" };
3749
+ let codeContent = "";
3750
+ for (let child = node.node.firstChild; child; child = child.nextSibling) {
3751
+ if (child.name === "CodeInfo") {
3752
+ infoProps = this.parseCodeInfo(view$1.state.sliceDoc(child.from, child.to).trim());
3753
+ }
3754
+ if (child.name === "CodeText") {
3755
+ codeContent = view$1.state.sliceDoc(child.from, child.to);
3756
+ }
3757
+ }
3758
+ const codeLines = [];
3759
+ for (let i = nodeLineStart.number + 1; i <= nodeLineEnd.number - 1; i++) {
3760
+ const codeLine = view$1.state.doc.line(i);
3761
+ codeLines.push(view$1.state.sliceDoc(codeLine.from, codeLine.to));
3762
+ }
3763
+ const totalCodeLines = nodeLineEnd.number - nodeLineStart.number - 1;
3764
+ const startLineNum = typeof infoProps.showLineNumbers === "number" ? infoProps.showLineNumbers : 1;
3765
+ const maxLineNum = startLineNum + totalCodeLines - 1;
3766
+ const lineNumWidth = Math.max(String(maxLineNum).length, String(startLineNum).length);
3767
+ const highlightInstanceCounters = new Array(infoProps.highlightText?.length ?? 0).fill(0);
3768
+ const diffStates = infoProps.diff ? this.analyzeDiffLines(codeLines) : [];
3769
+ const diffDisplayLineNumbers = infoProps.diff ? this.computeDiffDisplayLineNumbers(diffStates, startLineNum) : [];
3770
+ const displayLineNumbers = infoProps.diff ? diffDisplayLineNumbers.map((numbers, index) => numbers.newLine ?? numbers.oldLine ?? startLineNum + index) : codeLines.map((_, index) => startLineNum + index);
3771
+ const diffHighlightLineNumbers = infoProps.diff ? this.computeDiffDisplayLineNumbers(diffStates, startLineNum).map(
3772
+ (numbers, index) => numbers.newLine ?? numbers.oldLine ?? startLineNum + index
3773
+ ) : [];
3774
+ const maxOldDiffLineNum = diffDisplayLineNumbers.reduce((max, numbers) => {
3775
+ const oldLine = numbers.oldLine ?? 0;
3776
+ return oldLine > max ? oldLine : max;
3777
+ }, startLineNum);
3778
+ const maxNewDiffLineNum = diffDisplayLineNumbers.reduce((max, numbers) => {
3779
+ const newLine = numbers.newLine ?? 0;
3780
+ return newLine > max ? newLine : max;
3781
+ }, startLineNum);
3782
+ const diffOldLineNumWidth = Math.max(String(startLineNum).length, String(maxOldDiffLineNum).length);
3783
+ const diffNewLineNumWidth = Math.max(String(startLineNum).length, String(maxNewDiffLineNum).length);
3784
+ const shouldShowHeader = !cursorInRange && (infoProps.title || infoProps.copy || infoProps.language);
3785
+ const shouldShowCaption = !cursorInRange && !!infoProps.caption;
3786
+ if (shouldShowHeader) {
3787
+ decorations.push(
3788
+ view.Decoration.widget({
3789
+ widget: new CodeBlockHeaderWidget(infoProps, codeContent),
3790
+ block: false,
3791
+ side: -1
3792
+ }).range(nodeLineStart.from)
3793
+ );
3794
+ }
3795
+ let codeLineIndex = 0;
3796
+ for (let lineNumber = nodeLineStart.number; lineNumber <= nodeLineEnd.number; lineNumber++) {
3797
+ const line = view$1.state.doc.line(lineNumber);
3798
+ const isFenceLine = lineNumber === nodeLineStart.number || lineNumber === nodeLineEnd.number;
3799
+ const relativeLineNum = displayLineNumbers[codeLineIndex] ?? startLineNum + codeLineIndex;
3800
+ decorations.push(codeMarkDecorations["code-block-line"].range(line.from));
3801
+ if (lineNumber === nodeLineStart.number) {
3802
+ decorations.push(codeMarkDecorations["code-block-line-start"].range(line.from));
3803
+ if (shouldShowHeader) {
3804
+ decorations.push(view.Decoration.line({ class: "cm-draftly-code-block-has-header" }).range(line.from));
3805
+ }
3806
+ }
3807
+ if (lineNumber === nodeLineEnd.number) {
3808
+ decorations.push(codeMarkDecorations["code-block-line-end"].range(line.from));
3809
+ if (shouldShowCaption) {
3810
+ decorations.push(view.Decoration.line({ class: "cm-draftly-code-block-has-caption" }).range(line.from));
3811
+ }
3812
+ }
3813
+ if (!isFenceLine && infoProps.showLineNumbers && !infoProps.diff) {
3814
+ decorations.push(
3815
+ view.Decoration.line({
3816
+ class: "cm-draftly-code-line-numbered",
3817
+ attributes: {
3818
+ "data-line-num": String(relativeLineNum),
3819
+ style: `--line-num-width: ${lineNumWidth}ch`
2773
3820
  }
2774
- }
2775
- for (let child = node.node.firstChild; child; child = child.nextSibling) {
2776
- if (child.name === "CodeMark" || child.name === "CodeInfo") {
2777
- if (cursorInRange) {
2778
- decorations.push(codeMarkDecorations["code-fence"].range(child.from, child.to));
2779
- } else {
2780
- decorations.push(codeMarkDecorations["code-hidden"].range(child.from, child.to));
2781
- }
3821
+ }).range(line.from)
3822
+ );
3823
+ }
3824
+ if (!isFenceLine && infoProps.showLineNumbers && infoProps.diff) {
3825
+ const diffLineNumbers = diffDisplayLineNumbers[codeLineIndex];
3826
+ const diffState = diffStates[codeLineIndex];
3827
+ const diffMarker = diffState?.kind === "addition" ? "+" : diffState?.kind === "deletion" ? "-" : " ";
3828
+ decorations.push(
3829
+ view.Decoration.line({
3830
+ class: "cm-draftly-code-line-numbered-diff",
3831
+ attributes: {
3832
+ "data-line-num-old": diffLineNumbers?.oldLine != null ? String(diffLineNumbers.oldLine) : "",
3833
+ "data-line-num-new": diffLineNumbers?.newLine != null ? String(diffLineNumbers.newLine) : "",
3834
+ "data-diff-marker": diffMarker,
3835
+ style: `--line-num-old-width: ${diffOldLineNumWidth}ch; --line-num-new-width: ${diffNewLineNumWidth}ch`
2782
3836
  }
3837
+ }).range(line.from)
3838
+ );
3839
+ }
3840
+ if (!isFenceLine && infoProps.diff) {
3841
+ this.decorateDiffLine(
3842
+ line,
3843
+ codeLineIndex,
3844
+ diffStates,
3845
+ cursorInRange,
3846
+ !infoProps.showLineNumbers,
3847
+ decorations
3848
+ );
3849
+ }
3850
+ if (!isFenceLine && infoProps.highlightLines) {
3851
+ const highlightLineNumber = infoProps.diff ? diffHighlightLineNumbers[codeLineIndex] ?? codeLineIndex + 1 : startLineNum + codeLineIndex;
3852
+ if (infoProps.highlightLines.includes(highlightLineNumber)) {
3853
+ decorations.push(codeMarkDecorations["code-line-highlight"].range(line.from));
3854
+ }
3855
+ }
3856
+ if (!isFenceLine && infoProps.highlightText?.length) {
3857
+ this.decorateTextHighlights(
3858
+ line.from,
3859
+ view$1.state.sliceDoc(line.from, line.to),
3860
+ infoProps.highlightText,
3861
+ highlightInstanceCounters,
3862
+ decorations
3863
+ );
3864
+ }
3865
+ if (!isFenceLine) {
3866
+ codeLineIndex++;
3867
+ }
3868
+ }
3869
+ this.decorateFenceMarkers(node.node, cursorInRange, decorations);
3870
+ if (!cursorInRange && infoProps.caption) {
3871
+ decorations.push(
3872
+ view.Decoration.widget({
3873
+ widget: new CodeBlockCaptionWidget(infoProps.caption),
3874
+ block: false,
3875
+ side: 1
3876
+ }).range(nodeLineEnd.to)
3877
+ );
3878
+ }
3879
+ }
3880
+ decorateFenceMarkers(node, cursorInRange, decorations) {
3881
+ for (let child = node.firstChild; child; child = child.nextSibling) {
3882
+ if (child.name === "CodeMark" || child.name === "CodeInfo") {
3883
+ decorations.push(
3884
+ (cursorInRange ? codeMarkDecorations["code-fence"] : codeMarkDecorations["code-hidden"]).range(
3885
+ child.from,
3886
+ child.to
3887
+ )
3888
+ );
3889
+ }
3890
+ }
3891
+ }
3892
+ decorateDiffLine(line, codeLineIndex, diffStates, cursorInRange, showDiffMarkerGutter, decorations) {
3893
+ const diffState = diffStates[codeLineIndex];
3894
+ const diffMarker = diffState?.kind === "addition" ? "+" : diffState?.kind === "deletion" ? "-" : " ";
3895
+ if (showDiffMarkerGutter) {
3896
+ decorations.push(
3897
+ view.Decoration.line({
3898
+ class: "cm-draftly-code-line-diff-gutter",
3899
+ attributes: {
3900
+ "data-diff-marker": diffMarker
2783
3901
  }
2784
- if (!cursorInRange && infoProps.caption) {
2785
- decorations.push(
2786
- view.Decoration.widget({
2787
- widget: new CodeBlockCaptionWidget(infoProps.caption),
2788
- block: false,
2789
- side: 1
2790
- // After the content
2791
- }).range(nodeLineEnd.to)
2792
- );
3902
+ }).range(line.from)
3903
+ );
3904
+ }
3905
+ if (diffState?.kind === "addition") {
3906
+ decorations.push(codeMarkDecorations["diff-line-add"].range(line.from));
3907
+ if (cursorInRange && line.to > line.from) {
3908
+ decorations.push(codeMarkDecorations["diff-sign-add"].range(line.from, line.from + 1));
3909
+ }
3910
+ }
3911
+ if (diffState?.kind === "deletion") {
3912
+ decorations.push(codeMarkDecorations["diff-line-del"].range(line.from));
3913
+ if (cursorInRange && line.to > line.from) {
3914
+ decorations.push(codeMarkDecorations["diff-sign-del"].range(line.from, line.from + 1));
3915
+ }
3916
+ }
3917
+ if (!cursorInRange && line.to > line.from && (diffState?.escapedMarker || diffState?.kind === "addition" || diffState?.kind === "deletion")) {
3918
+ decorations.push(codeMarkDecorations["diff-escape-hidden"].range(line.from, line.from + 1));
3919
+ }
3920
+ if (diffState?.modificationRanges?.length) {
3921
+ for (const [start, end] of diffState.modificationRanges) {
3922
+ const rangeFrom = line.from + diffState.contentOffset + start;
3923
+ const rangeTo = line.from + diffState.contentOffset + end;
3924
+ if (rangeTo > rangeFrom) {
3925
+ decorations.push(
3926
+ (diffState.kind === "addition" ? codeMarkDecorations["diff-mod-add"] : codeMarkDecorations["diff-mod-del"]).range(rangeFrom, rangeTo)
3927
+ );
3928
+ }
3929
+ }
3930
+ }
3931
+ }
3932
+ decorateTextHighlights(lineFrom, lineText, highlights, instanceCounters, decorations) {
3933
+ for (const [highlightIndex, textHighlight] of highlights.entries()) {
3934
+ try {
3935
+ const regex = new RegExp(textHighlight.pattern, "g");
3936
+ let match;
3937
+ while ((match = regex.exec(lineText)) !== null) {
3938
+ instanceCounters[highlightIndex] = (instanceCounters[highlightIndex] ?? 0) + 1;
3939
+ const globalMatchIndex = instanceCounters[highlightIndex];
3940
+ const shouldHighlight = !textHighlight.instances || textHighlight.instances.includes(globalMatchIndex);
3941
+ if (shouldHighlight) {
3942
+ const matchFrom = lineFrom + match.index;
3943
+ const matchTo = matchFrom + match[0].length;
3944
+ decorations.push(codeMarkDecorations["code-text-highlight"].range(matchFrom, matchTo));
2793
3945
  }
2794
3946
  }
3947
+ } catch {
2795
3948
  }
2796
- });
3949
+ }
2797
3950
  }
2798
3951
  /**
2799
3952
  * Render code elements to HTML for static preview.
2800
3953
  * Applies syntax highlighting using @lezer/highlight.
2801
3954
  */
2802
- renderToHTML(node, children, ctx) {
3955
+ async renderToHTML(node, _children, ctx) {
2803
3956
  if (node.name === "CodeMark") {
2804
3957
  return "";
2805
3958
  }
@@ -2809,7 +3962,7 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2809
3962
  if (match && match[1]) {
2810
3963
  content = match[1];
2811
3964
  }
2812
- return `<code class="cm-draftly-code-inline" style="padding: 0.1rem 0.25rem">${ctx.sanitize(content)}</code>`;
3965
+ return `<code class="cm-draftly-code-inline" style="padding: 0.1rem 0.25rem">${this.escapeHtml(content)}</code>`;
2813
3966
  }
2814
3967
  if (node.name === "FencedCode") {
2815
3968
  const content = ctx.sliceDoc(node.from, node.to);
@@ -2827,9 +3980,9 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2827
3980
  html += `<div class="cm-draftly-code-header">`;
2828
3981
  html += `<div class="cm-draftly-code-header-left">`;
2829
3982
  if (props.title) {
2830
- html += `<span class="cm-draftly-code-header-title">${ctx.sanitize(props.title)}</span>`;
3983
+ html += `<span class="cm-draftly-code-header-title">${this.escapeHtml(props.title)}</span>`;
2831
3984
  } else if (props.language) {
2832
- html += `<span class="cm-draftly-code-header-lang">${ctx.sanitize(props.language)}</span>`;
3985
+ html += `<span class="cm-draftly-code-header-lang">${this.escapeHtml(props.language)}</span>`;
2833
3986
  }
2834
3987
  html += `</div>`;
2835
3988
  if (props.copy !== false) {
@@ -2842,32 +3995,83 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2842
3995
  }
2843
3996
  html += `</div>`;
2844
3997
  }
2845
- const startLineNum = typeof props.lineNumbers === "number" ? props.lineNumbers : 1;
2846
- const lineNumWidth = String(startLineNum + codeLines.length - 1).length;
3998
+ const startLineNum = typeof props.showLineNumbers === "number" ? props.showLineNumbers : 1;
3999
+ const previewHighlightCounters = new Array(props.highlightText?.length ?? 0).fill(0);
4000
+ const diffStates = props.diff ? this.analyzeDiffLines(codeLines) : [];
4001
+ const previewDiffLineNumbers = props.diff ? this.computeDiffDisplayLineNumbers(diffStates, startLineNum) : [];
4002
+ const previewLineNumbers = props.diff ? previewDiffLineNumbers.map((numbers, index) => numbers.newLine ?? numbers.oldLine ?? startLineNum + index) : codeLines.map((_, index) => startLineNum + index);
4003
+ const previewHighlightLineNumbers = props.diff ? this.computeDiffDisplayLineNumbers(diffStates, startLineNum).map(
4004
+ (numbers, index) => numbers.newLine ?? numbers.oldLine ?? startLineNum + index
4005
+ ) : [];
4006
+ const lineNumWidth = String(Math.max(...previewLineNumbers, startLineNum)).length;
4007
+ const previewOldLineNumWidth = String(
4008
+ Math.max(
4009
+ ...previewDiffLineNumbers.map((numbers) => numbers.oldLine ?? 0),
4010
+ startLineNum
4011
+ )
4012
+ ).length;
4013
+ const previewNewLineNumWidth = String(
4014
+ Math.max(
4015
+ ...previewDiffLineNumbers.map((numbers) => numbers.newLine ?? 0),
4016
+ startLineNum
4017
+ )
4018
+ ).length;
4019
+ const previewContentLines = props.diff ? diffStates.map((state) => state.content) : codeLines;
4020
+ const highlightedLines = await this.highlightCodeLines(
4021
+ previewContentLines.join("\n"),
4022
+ props.language || "",
4023
+ ctx.syntaxHighlighters
4024
+ );
2847
4025
  const hasHeader = showHeader ? " cm-draftly-code-block-has-header" : "";
2848
4026
  const hasCaption = props.caption ? " cm-draftly-code-block-has-caption" : "";
2849
- html += `<pre class="cm-draftly-code-block${hasHeader}${hasCaption}"${props.language ? ` data-lang="${ctx.sanitize(props.language)}"` : ""}>`;
4027
+ html += `<pre class="cm-draftly-code-block${hasHeader}${hasCaption}"${props.language ? ` data-lang="${this.escapeAttribute(props.language)}"` : ""}>`;
2850
4028
  html += `<code>`;
2851
4029
  codeLines.forEach((line, index) => {
2852
- const lineNum = startLineNum + index;
2853
- const isHighlighted = props.highlightLines?.includes(index + 1);
4030
+ const lineNum = previewLineNumbers[index] ?? startLineNum + index;
4031
+ const highlightLineNumber = props.diff ? previewHighlightLineNumbers[index] ?? startLineNum + index : startLineNum + index;
4032
+ const isHighlighted = props.highlightLines?.includes(highlightLineNumber);
4033
+ const diffState = props.diff ? diffStates[index] : void 0;
4034
+ const diffLineNumbers = props.diff ? previewDiffLineNumbers[index] : void 0;
2854
4035
  const lineClasses = ["cm-draftly-code-line"];
2855
4036
  if (isHighlighted) lineClasses.push("cm-draftly-code-line-highlight");
2856
- if (props.lineNumbers) lineClasses.push("cm-draftly-code-line-numbered");
4037
+ if (props.showLineNumbers) {
4038
+ lineClasses.push(props.diff ? "cm-draftly-code-line-numbered-diff" : "cm-draftly-code-line-numbered");
4039
+ }
4040
+ if (diffState?.kind === "addition") lineClasses.push("cm-draftly-code-line-diff-add");
4041
+ if (diffState?.kind === "deletion") lineClasses.push("cm-draftly-code-line-diff-del");
2857
4042
  const lineAttrs = [`class="${lineClasses.join(" ")}"`];
2858
- if (props.lineNumbers) {
4043
+ if (props.showLineNumbers && !props.diff) {
2859
4044
  lineAttrs.push(`data-line-num="${lineNum}"`);
2860
4045
  lineAttrs.push(`style="--line-num-width: ${lineNumWidth}ch"`);
2861
4046
  }
2862
- let lineContent = this.highlightCodeLine(line, props.language || "", ctx);
4047
+ if (props.diff) {
4048
+ const diffMarker = diffState?.kind === "addition" ? "+" : diffState?.kind === "deletion" ? "-" : " ";
4049
+ if (props.showLineNumbers) {
4050
+ lineAttrs.push(`data-line-num-old="${diffLineNumbers?.oldLine != null ? diffLineNumbers.oldLine : ""}"`);
4051
+ lineAttrs.push(`data-line-num-new="${diffLineNumbers?.newLine != null ? diffLineNumbers.newLine : ""}"`);
4052
+ lineAttrs.push(`data-diff-marker="${diffMarker}"`);
4053
+ lineAttrs.push(
4054
+ `style="--line-num-old-width: ${previewOldLineNumWidth}ch; --line-num-new-width: ${previewNewLineNumWidth}ch"`
4055
+ );
4056
+ } else {
4057
+ lineAttrs.push(`data-diff-marker="${diffMarker}"`);
4058
+ lineClasses.push("cm-draftly-code-line-diff-gutter");
4059
+ lineAttrs[0] = `class="${lineClasses.join(" ")}"`;
4060
+ }
4061
+ }
4062
+ const highlightedLine = highlightedLines[index] ?? this.escapeHtml(previewContentLines[index] ?? line);
4063
+ let lineContent = highlightedLine;
4064
+ if (diffState) {
4065
+ lineContent = this.renderDiffPreviewLine(diffState, highlightedLine);
4066
+ }
2863
4067
  if (props.highlightText && props.highlightText.length > 0) {
2864
- lineContent = this.applyTextHighlights(lineContent, props.highlightText);
4068
+ lineContent = this.applyTextHighlights(lineContent, props.highlightText, previewHighlightCounters);
2865
4069
  }
2866
4070
  html += `<span ${lineAttrs.join(" ")}>${lineContent || " "}</span>`;
2867
4071
  });
2868
4072
  html += `</code></pre>`;
2869
4073
  if (props.caption) {
2870
- html += `<div class="cm-draftly-code-caption">${ctx.sanitize(props.caption)}</div>`;
4074
+ html += `<div class="cm-draftly-code-caption">${this.escapeHtml(props.caption)}</div>`;
2871
4075
  }
2872
4076
  html += `</div>`;
2873
4077
  return html;
@@ -2877,54 +4081,290 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2877
4081
  }
2878
4082
  return null;
2879
4083
  }
4084
+ /** Parse comma-separated numbers and ranges (e.g. "1,3-5") into [1,3,4,5]. */
4085
+ parseNumberList(value) {
4086
+ const result = [];
4087
+ for (const part of value.split(",")) {
4088
+ const trimmed = part.trim();
4089
+ const rangeMatch = trimmed.match(/^(\d+)-(\d+)$/);
4090
+ if (rangeMatch && rangeMatch[1] && rangeMatch[2]) {
4091
+ const start = parseInt(rangeMatch[1], 10);
4092
+ const end = parseInt(rangeMatch[2], 10);
4093
+ for (let i = start; i <= end; i++) {
4094
+ result.push(i);
4095
+ }
4096
+ continue;
4097
+ }
4098
+ if (/^\d+$/.test(trimmed)) {
4099
+ result.push(parseInt(trimmed, 10));
4100
+ }
4101
+ }
4102
+ return result;
4103
+ }
2880
4104
  /**
2881
4105
  * Highlight a single line of code using the language's Lezer parser.
2882
4106
  * Falls back to sanitized plain text if the language is not supported.
2883
4107
  */
2884
- highlightCodeLine(line, lang, ctx) {
2885
- if (!lang || !line) {
2886
- return ctx.sanitize(line);
4108
+ async highlightCodeLines(code, lang, syntaxHighlighters) {
4109
+ const rawLines = code.split("\n");
4110
+ if (!lang || !code) {
4111
+ return rawLines.map((line) => this.escapeHtml(line));
2887
4112
  }
2888
- const langDesc = languageData.languages.find(
2889
- (l) => l.name.toLowerCase() === lang.toLowerCase() || l.alias && l.alias.includes(lang.toLowerCase())
2890
- );
2891
- if (!langDesc || !langDesc.support) {
2892
- return ctx.sanitize(line);
4113
+ const parser = await this.resolveLanguageParser(lang);
4114
+ if (!parser) {
4115
+ return rawLines.map((line) => this.escapeHtml(line));
2893
4116
  }
2894
4117
  try {
2895
- const parser = langDesc.support.language.parser;
2896
- const tree = parser.parse(line);
2897
- let result = "";
4118
+ const tree = parser.parse(code);
4119
+ const highlightedLines = [""];
2898
4120
  highlight.highlightCode(
2899
- line,
4121
+ code,
2900
4122
  tree,
2901
- highlight.classHighlighter,
4123
+ syntaxHighlighters && syntaxHighlighters.length > 0 ? syntaxHighlighters : [],
2902
4124
  (text, classes2) => {
2903
- if (classes2) {
2904
- result += `<span class="${classes2}">${ctx.sanitize(text)}</span>`;
2905
- } else {
2906
- result += ctx.sanitize(text);
2907
- }
4125
+ const chunk = classes2 ? `<span class="${this.escapeAttribute(classes2)}">${this.escapeHtml(text)}</span>` : this.escapeHtml(text);
4126
+ highlightedLines[highlightedLines.length - 1] += chunk;
2908
4127
  },
2909
4128
  () => {
4129
+ highlightedLines.push("");
2910
4130
  }
2911
- // No newlines for single line
2912
4131
  );
2913
- return result;
4132
+ return rawLines.map((line, index) => highlightedLines[index] || this.escapeHtml(line));
2914
4133
  } catch {
2915
- return ctx.sanitize(line);
4134
+ return rawLines.map((line) => this.escapeHtml(line));
2916
4135
  }
2917
4136
  }
4137
+ async resolveLanguageParser(lang) {
4138
+ const normalizedLang = this.normalizeLanguage(lang);
4139
+ if (!normalizedLang) return null;
4140
+ const cached = this.parserCache.get(normalizedLang);
4141
+ if (cached) return cached;
4142
+ const parserPromise = (async () => {
4143
+ const langDesc = language.LanguageDescription.matchLanguageName(languageData.languages, normalizedLang, true);
4144
+ if (!langDesc) return null;
4145
+ if (langDesc.support) {
4146
+ return langDesc.support.language.parser;
4147
+ }
4148
+ if (typeof langDesc.load === "function") {
4149
+ try {
4150
+ const support = await langDesc.load();
4151
+ return support.language.parser;
4152
+ } catch {
4153
+ return null;
4154
+ }
4155
+ }
4156
+ return null;
4157
+ })();
4158
+ this.parserCache.set(normalizedLang, parserPromise);
4159
+ return parserPromise;
4160
+ }
4161
+ normalizeLanguage(lang) {
4162
+ const normalized = lang.trim().toLowerCase();
4163
+ if (!normalized) return "";
4164
+ const normalizedMap = {
4165
+ "c++": "cpp",
4166
+ "c#": "csharp",
4167
+ "f#": "fsharp",
4168
+ py: "python",
4169
+ js: "javascript",
4170
+ ts: "typescript",
4171
+ sh: "shell"
4172
+ };
4173
+ return normalizedMap[normalized] ?? normalized;
4174
+ }
4175
+ escapeHtml(value) {
4176
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
4177
+ }
4178
+ escapeAttribute(value) {
4179
+ return this.escapeHtml(value).replace(/`/g, "&#96;");
4180
+ }
4181
+ analyzeDiffLines(lines) {
4182
+ const states = lines.map((line) => this.parseDiffLineState(line));
4183
+ let index = 0;
4184
+ while (index < states.length) {
4185
+ if (states[index]?.kind !== "deletion") {
4186
+ index++;
4187
+ continue;
4188
+ }
4189
+ const deletionStart = index;
4190
+ while (index < states.length && states[index]?.kind === "deletion") {
4191
+ index++;
4192
+ }
4193
+ const deletionEnd = index;
4194
+ const additionStart = index;
4195
+ while (index < states.length && states[index]?.kind === "addition") {
4196
+ index++;
4197
+ }
4198
+ const additionEnd = index;
4199
+ if (additionStart === additionEnd) {
4200
+ continue;
4201
+ }
4202
+ const pairCount = Math.min(deletionEnd - deletionStart, additionEnd - additionStart);
4203
+ for (let pairIndex = 0; pairIndex < pairCount; pairIndex++) {
4204
+ const deletionState = states[deletionStart + pairIndex];
4205
+ const additionState = states[additionStart + pairIndex];
4206
+ if (!deletionState || !additionState) {
4207
+ continue;
4208
+ }
4209
+ const ranges = this.computeChangedRanges(deletionState.content, additionState.content);
4210
+ if (ranges.oldRanges.length > 0) {
4211
+ deletionState.modificationRanges = ranges.oldRanges;
4212
+ }
4213
+ if (ranges.newRanges.length > 0) {
4214
+ additionState.modificationRanges = ranges.newRanges;
4215
+ }
4216
+ }
4217
+ }
4218
+ return states;
4219
+ }
4220
+ computeDiffDisplayLineNumbers(states, startLineNum) {
4221
+ const numbers = [];
4222
+ let oldLineNumber = startLineNum;
4223
+ let newLineNumber = startLineNum;
4224
+ for (const state of states) {
4225
+ if (state.kind === "deletion") {
4226
+ numbers.push({ oldLine: oldLineNumber, newLine: null });
4227
+ oldLineNumber++;
4228
+ continue;
4229
+ }
4230
+ if (state.kind === "addition") {
4231
+ numbers.push({ oldLine: null, newLine: newLineNumber });
4232
+ newLineNumber++;
4233
+ continue;
4234
+ }
4235
+ numbers.push({ oldLine: oldLineNumber, newLine: newLineNumber });
4236
+ oldLineNumber++;
4237
+ newLineNumber++;
4238
+ }
4239
+ return numbers;
4240
+ }
4241
+ parseDiffLineState(line) {
4242
+ const escapedMarker = line.startsWith("\\+") || line.startsWith("\\-");
4243
+ if (escapedMarker) {
4244
+ return {
4245
+ kind: "normal",
4246
+ content: line.slice(1),
4247
+ contentOffset: 1,
4248
+ escapedMarker: true
4249
+ };
4250
+ }
4251
+ if (line.startsWith("+")) {
4252
+ return {
4253
+ kind: "addition",
4254
+ content: line.slice(1),
4255
+ contentOffset: 1,
4256
+ escapedMarker: false
4257
+ };
4258
+ }
4259
+ if (line.startsWith("-")) {
4260
+ return {
4261
+ kind: "deletion",
4262
+ content: line.slice(1),
4263
+ contentOffset: 1,
4264
+ escapedMarker: false
4265
+ };
4266
+ }
4267
+ return {
4268
+ kind: "normal",
4269
+ content: line,
4270
+ contentOffset: 0,
4271
+ escapedMarker: false
4272
+ };
4273
+ }
4274
+ computeChangedRanges(oldText, newText) {
4275
+ let prefix = 0;
4276
+ while (prefix < oldText.length && prefix < newText.length && oldText[prefix] === newText[prefix]) {
4277
+ prefix++;
4278
+ }
4279
+ let oldSuffix = oldText.length;
4280
+ let newSuffix = newText.length;
4281
+ while (oldSuffix > prefix && newSuffix > prefix && oldText[oldSuffix - 1] === newText[newSuffix - 1]) {
4282
+ oldSuffix--;
4283
+ newSuffix--;
4284
+ }
4285
+ const oldRanges = [];
4286
+ const newRanges = [];
4287
+ if (oldSuffix > prefix) {
4288
+ oldRanges.push([prefix, oldSuffix]);
4289
+ }
4290
+ if (newSuffix > prefix) {
4291
+ newRanges.push([prefix, newSuffix]);
4292
+ }
4293
+ return { oldRanges, newRanges };
4294
+ }
4295
+ renderDiffPreviewLine(diffState, highlightedContent) {
4296
+ const modClass = diffState.kind === "addition" ? "cm-draftly-code-diff-mod-add" : diffState.kind === "deletion" ? "cm-draftly-code-diff-mod-del" : "";
4297
+ const baseHighlightedContent = highlightedContent || this.escapeHtml(diffState.content);
4298
+ const contentHtml = diffState.modificationRanges && modClass ? this.applyRangesToHighlightedHTML(baseHighlightedContent, diffState.modificationRanges, modClass) : baseHighlightedContent;
4299
+ return contentHtml || " ";
4300
+ }
4301
+ applyRangesToHighlightedHTML(htmlContent, ranges, className) {
4302
+ const normalizedRanges = ranges.map(([start, end]) => [Math.max(0, start), Math.max(0, end)]).filter(([start, end]) => end > start).sort((a, b) => a[0] - b[0]);
4303
+ if (normalizedRanges.length === 0 || !htmlContent) {
4304
+ return htmlContent;
4305
+ }
4306
+ const isInsideRange = (position) => {
4307
+ for (const [start, end] of normalizedRanges) {
4308
+ if (position >= start && position < end) return true;
4309
+ if (position < start) return false;
4310
+ }
4311
+ return false;
4312
+ };
4313
+ let result = "";
4314
+ let htmlIndex = 0;
4315
+ let textPosition = 0;
4316
+ let markOpen = false;
4317
+ while (htmlIndex < htmlContent.length) {
4318
+ const char = htmlContent[htmlIndex];
4319
+ if (char === "<") {
4320
+ const tagEnd = htmlContent.indexOf(">", htmlIndex);
4321
+ if (tagEnd === -1) {
4322
+ result += htmlContent.slice(htmlIndex);
4323
+ break;
4324
+ }
4325
+ result += htmlContent.slice(htmlIndex, tagEnd + 1);
4326
+ htmlIndex = tagEnd + 1;
4327
+ continue;
4328
+ }
4329
+ let token = char;
4330
+ if (char === "&") {
4331
+ const entityEnd = htmlContent.indexOf(";", htmlIndex);
4332
+ if (entityEnd !== -1) {
4333
+ token = htmlContent.slice(htmlIndex, entityEnd + 1);
4334
+ htmlIndex = entityEnd + 1;
4335
+ } else {
4336
+ htmlIndex += 1;
4337
+ }
4338
+ } else {
4339
+ htmlIndex += 1;
4340
+ }
4341
+ const shouldMark = isInsideRange(textPosition);
4342
+ if (shouldMark && !markOpen) {
4343
+ result += `<mark class="${className}">`;
4344
+ markOpen = true;
4345
+ }
4346
+ if (!shouldMark && markOpen) {
4347
+ result += "</mark>";
4348
+ markOpen = false;
4349
+ }
4350
+ result += token;
4351
+ textPosition += 1;
4352
+ }
4353
+ if (markOpen) {
4354
+ result += "</mark>";
4355
+ }
4356
+ return result;
4357
+ }
2918
4358
  /**
2919
4359
  * Apply text highlights (regex patterns) to already syntax-highlighted HTML.
2920
4360
  * Wraps matched patterns in `<mark>` elements.
2921
4361
  */
2922
- applyTextHighlights(htmlContent, highlights) {
4362
+ applyTextHighlights(htmlContent, highlights, instanceCounters) {
2923
4363
  let result = htmlContent;
2924
- for (const highlight of highlights) {
4364
+ for (const [highlightIndex, highlight] of highlights.entries()) {
2925
4365
  try {
2926
4366
  const regex = new RegExp(`(${highlight.pattern})`, "g");
2927
- let matchCount = 0;
4367
+ let matchCount = instanceCounters?.[highlightIndex] ?? 0;
2928
4368
  result = result.replace(regex, (match) => {
2929
4369
  matchCount++;
2930
4370
  const shouldHighlight = !highlight.instances || highlight.instances.includes(matchCount);
@@ -2933,252 +4373,15 @@ var CodePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
2933
4373
  }
2934
4374
  return match;
2935
4375
  });
4376
+ if (instanceCounters) {
4377
+ instanceCounters[highlightIndex] = matchCount;
4378
+ }
2936
4379
  } catch {
2937
4380
  }
2938
4381
  }
2939
4382
  return result;
2940
4383
  }
2941
4384
  };
2942
- var theme10 = chunkKDEDLC3D_cjs.createTheme({
2943
- default: {
2944
- // Inline code
2945
- ".cm-draftly-code-inline": {
2946
- fontFamily: "var(--font-jetbrains-mono, monospace)",
2947
- fontSize: "0.9rem",
2948
- backgroundColor: "rgba(0, 0, 0, 0.05)",
2949
- padding: "0.1rem 0.25rem",
2950
- border: "1px solid var(--color-border)",
2951
- borderRadius: "3px"
2952
- },
2953
- // Fenced code block lines
2954
- ".cm-draftly-code-block-line": {
2955
- "--radius": "0.375rem",
2956
- fontFamily: "var(--font-jetbrains-mono, monospace)",
2957
- fontSize: "0.9rem",
2958
- backgroundColor: "rgba(0, 0, 0, 0.03)",
2959
- padding: "0 1rem !important",
2960
- lineHeight: "1.5",
2961
- borderLeft: "1px solid var(--color-border)",
2962
- borderRight: "1px solid var(--color-border)"
2963
- },
2964
- // First line of code block
2965
- ".cm-draftly-code-block-line-start": {
2966
- borderTopLeftRadius: "var(--radius)",
2967
- borderTopRightRadius: "var(--radius)",
2968
- position: "relative",
2969
- overflow: "hidden",
2970
- borderTop: "1px solid var(--color-border)",
2971
- paddingBottom: "0.5rem !important"
2972
- },
2973
- // Remove top radius when header is present
2974
- ".cm-draftly-code-block-has-header": {
2975
- padding: "0 !important",
2976
- paddingBottom: "0.5rem !important"
2977
- },
2978
- // Code block header widget
2979
- ".cm-draftly-code-header": {
2980
- display: "flex",
2981
- justifyContent: "space-between",
2982
- alignItems: "center",
2983
- padding: "0.25rem 1rem",
2984
- backgroundColor: "rgba(0, 0, 0, 0.06)",
2985
- fontFamily: "var(--font-jetbrains-mono, monospace)",
2986
- fontSize: "0.85rem"
2987
- },
2988
- ".cm-draftly-code-header-left": {
2989
- display: "flex",
2990
- alignItems: "center",
2991
- gap: "0.5rem"
2992
- },
2993
- ".cm-draftly-code-header-title": {
2994
- color: "var(--color-text, inherit)",
2995
- fontWeight: "500"
2996
- },
2997
- ".cm-draftly-code-header-lang": {
2998
- color: "#6a737d",
2999
- opacity: "0.8"
3000
- },
3001
- ".cm-draftly-code-header-right": {
3002
- display: "flex",
3003
- alignItems: "center",
3004
- gap: "0.5rem"
3005
- },
3006
- ".cm-draftly-code-copy-btn": {
3007
- display: "flex",
3008
- alignItems: "center",
3009
- justifyContent: "center",
3010
- padding: "0.25rem",
3011
- backgroundColor: "transparent",
3012
- border: "none",
3013
- borderRadius: "4px",
3014
- cursor: "pointer",
3015
- color: "#6a737d",
3016
- transition: "color 0.2s, background-color 0.2s"
3017
- },
3018
- ".cm-draftly-code-copy-btn:hover": {
3019
- backgroundColor: "rgba(0, 0, 0, 0.1)",
3020
- color: "var(--color-text, inherit)"
3021
- },
3022
- ".cm-draftly-code-copy-btn.copied": {
3023
- color: "#22c55e"
3024
- },
3025
- // Caption (below code block)
3026
- ".cm-draftly-code-block-has-caption": {
3027
- padding: "0 !important",
3028
- paddingTop: "0.5rem !important"
3029
- },
3030
- ".cm-draftly-code-caption": {
3031
- textAlign: "center",
3032
- fontSize: "0.85rem",
3033
- color: "#6a737d",
3034
- fontStyle: "italic",
3035
- padding: "0.25rem 1rem",
3036
- backgroundColor: "rgba(0, 0, 0, 0.06)"
3037
- },
3038
- // Last line of code block
3039
- ".cm-draftly-code-block-line-end": {
3040
- borderBottomLeftRadius: "var(--radius)",
3041
- borderBottomRightRadius: "var(--radius)",
3042
- borderBottom: "1px solid var(--color-border)",
3043
- paddingTop: "0.5rem !important"
3044
- },
3045
- ".cm-draftly-code-block-line-end br": {
3046
- display: "none"
3047
- },
3048
- // Fence markers (```)
3049
- ".cm-draftly-code-fence": {
3050
- color: "#6a737d",
3051
- fontFamily: "var(--font-jetbrains-mono, monospace)"
3052
- },
3053
- // Line numbers
3054
- ".cm-draftly-code-line-numbered": {
3055
- paddingLeft: "calc(var(--line-num-width, 2ch) + 1rem) !important",
3056
- position: "relative"
3057
- },
3058
- ".cm-draftly-code-line-numbered::before": {
3059
- content: "attr(data-line-num)",
3060
- position: "absolute",
3061
- left: "0.5rem",
3062
- top: "0.2rem",
3063
- width: "var(--line-num-width, 2ch)",
3064
- textAlign: "right",
3065
- color: "#6a737d",
3066
- opacity: "0.6",
3067
- fontFamily: "var(--font-jetbrains-mono, monospace)",
3068
- fontSize: "0.85rem",
3069
- userSelect: "none"
3070
- },
3071
- // Preview: code lines (need block display for full-width highlights)
3072
- ".cm-draftly-code-line": {
3073
- display: "block",
3074
- position: "relative",
3075
- paddingLeft: "1rem",
3076
- paddingRight: "1rem",
3077
- lineHeight: "1.5",
3078
- borderLeft: "3px solid transparent"
3079
- },
3080
- // Line highlight
3081
- ".cm-draftly-code-line-highlight": {
3082
- backgroundColor: "rgba(255, 220, 100, 0.2) !important",
3083
- borderLeft: "3px solid #f0b429 !important"
3084
- },
3085
- // Text highlight
3086
- ".cm-draftly-code-text-highlight": {
3087
- backgroundColor: "rgba(255, 220, 100, 0.4)",
3088
- borderRadius: "2px",
3089
- padding: "0.1rem 0"
3090
- },
3091
- // Preview: container wrapper
3092
- ".cm-draftly-code-container": {
3093
- margin: "1rem 0",
3094
- borderRadius: "var(--radius)",
3095
- overflow: "hidden",
3096
- border: "1px solid var(--color-border)"
3097
- },
3098
- // Preview: header inside container
3099
- ".cm-draftly-code-container .cm-draftly-code-header": {
3100
- borderRadius: "0",
3101
- border: "none",
3102
- borderBottom: "1px solid var(--color-border)"
3103
- },
3104
- // Preview: code block inside container
3105
- ".cm-draftly-code-container .cm-draftly-code-block": {
3106
- margin: "0",
3107
- borderRadius: "0",
3108
- border: "none",
3109
- whiteSpace: "pre-wrap"
3110
- },
3111
- // Preview: caption inside container
3112
- ".cm-draftly-code-container .cm-draftly-code-caption": {
3113
- borderTop: "1px solid var(--color-border)"
3114
- },
3115
- // Preview: standalone code block (not in container)
3116
- ".cm-draftly-code-block": {
3117
- fontFamily: "var(--font-jetbrains-mono, monospace)",
3118
- fontSize: "0.9rem",
3119
- backgroundColor: "rgba(0, 0, 0, 0.03)",
3120
- padding: "1rem",
3121
- overflow: "auto",
3122
- position: "relative",
3123
- borderRadius: "var(--radius)",
3124
- border: "1px solid var(--color-border)"
3125
- },
3126
- // Preview: code block with header (remove top radius)
3127
- ".cm-draftly-code-block.cm-draftly-code-block-has-header": {
3128
- borderTopLeftRadius: "0",
3129
- borderTopRightRadius: "0",
3130
- borderTop: "none",
3131
- margin: "0",
3132
- paddingTop: "0.5rem !important"
3133
- },
3134
- // Preview: code block with caption (remove bottom radius)
3135
- ".cm-draftly-code-block.cm-draftly-code-block-has-caption": {
3136
- borderBottomLeftRadius: "0",
3137
- borderBottomRightRadius: "0",
3138
- borderBottom: "none",
3139
- paddingBottom: "0.5rem !important"
3140
- }
3141
- },
3142
- dark: {
3143
- ".cm-draftly-code-inline": {
3144
- backgroundColor: "rgba(255, 255, 255, 0.1)"
3145
- },
3146
- ".cm-draftly-code-block-line": {
3147
- backgroundColor: "rgba(255, 255, 255, 0.05)"
3148
- },
3149
- ".cm-draftly-code-fence": {
3150
- color: "#8b949e"
3151
- },
3152
- ".cm-draftly-code-block": {
3153
- backgroundColor: "rgba(255, 255, 255, 0.05)"
3154
- },
3155
- ".cm-draftly-code-header": {
3156
- backgroundColor: "rgba(255, 255, 255, 0.08)"
3157
- },
3158
- ".cm-draftly-code-header-lang": {
3159
- color: "#8b949e"
3160
- },
3161
- ".cm-draftly-code-copy-btn": {
3162
- color: "#8b949e"
3163
- },
3164
- ".cm-draftly-code-copy-btn:hover": {
3165
- backgroundColor: "rgba(255, 255, 255, 0.1)"
3166
- },
3167
- ".cm-draftly-code-caption": {
3168
- backgroundColor: "rgba(255, 255, 255, 0.05)"
3169
- },
3170
- ".cm-draftly-code-line-numbered::before": {
3171
- color: "#8b949e"
3172
- },
3173
- ".cm-draftly-code-line-highlight": {
3174
- backgroundColor: "rgba(255, 220, 100, 0.15) !important",
3175
- borderLeft: "3px solid #d9a520 !important"
3176
- },
3177
- ".cm-draftly-code-text-highlight": {
3178
- backgroundColor: "rgba(255, 220, 100, 0.3)"
3179
- }
3180
- }
3181
- });
3182
4385
  var quoteMarkDecorations = {
3183
4386
  /** Decoration for the > marker */
3184
4387
  "quote-mark": view.Decoration.replace({}),
@@ -3189,7 +4392,7 @@ var quoteLineDecorations = {
3189
4392
  /** Decoration for blockquote lines */
3190
4393
  "quote-line": view.Decoration.line({ class: "cm-draftly-quote-line" })
3191
4394
  };
3192
- var QuotePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
4395
+ var QuotePlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
3193
4396
  name = "quote";
3194
4397
  version = "1.0.0";
3195
4398
  decorationPriority = 10;
@@ -3260,7 +4463,7 @@ var QuotePlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
3260
4463
  `;
3261
4464
  }
3262
4465
  };
3263
- var theme11 = chunkKDEDLC3D_cjs.createTheme({
4466
+ var theme11 = chunkTBVZEK2H_cjs.createTheme({
3264
4467
  default: {
3265
4468
  // Line styling with left border
3266
4469
  ".cm-draftly-quote-line": {
@@ -3279,7 +4482,7 @@ var theme11 = chunkKDEDLC3D_cjs.createTheme({
3279
4482
  });
3280
4483
  var hrLineDecoration = view.Decoration.line({ class: "cm-draftly-hr-line" });
3281
4484
  var hrMarkDecoration = view.Decoration.replace({});
3282
- var HRPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
4485
+ var HRPlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
3283
4486
  name = "hr";
3284
4487
  version = "1.0.0";
3285
4488
  decorationPriority = 10;
@@ -3327,7 +4530,7 @@ var HRPlugin = class extends chunk72ZYRGRT_cjs.DecorationPlugin {
3327
4530
  `;
3328
4531
  }
3329
4532
  };
3330
- var theme12 = chunkKDEDLC3D_cjs.createTheme({
4533
+ var theme12 = chunkTBVZEK2H_cjs.createTheme({
3331
4534
  default: {
3332
4535
  // Line styling — displays a centered horizontal line
3333
4536
  ".cm-draftly-hr-line": {
@@ -3346,6 +4549,103 @@ var theme12 = chunkKDEDLC3D_cjs.createTheme({
3346
4549
  }
3347
4550
  }
3348
4551
  });
4552
+ function shortcodeToEmoji(raw) {
4553
+ const rendered = emoji__namespace.emojify(raw);
4554
+ return rendered !== raw ? rendered : null;
4555
+ }
4556
+ var EmojiWidget = class extends view.WidgetType {
4557
+ constructor(rendered) {
4558
+ super();
4559
+ this.rendered = rendered;
4560
+ }
4561
+ eq(other) {
4562
+ return other.rendered === this.rendered;
4563
+ }
4564
+ toDOM() {
4565
+ const span = document.createElement("span");
4566
+ span.className = "cm-draftly-emoji";
4567
+ span.textContent = this.rendered;
4568
+ return span;
4569
+ }
4570
+ ignoreEvent() {
4571
+ return false;
4572
+ }
4573
+ };
4574
+ var emojiMarkDecorations = {
4575
+ "emoji-source": view.Decoration.mark({ class: "cm-draftly-emoji-source" })
4576
+ };
4577
+ var EmojiPlugin = class extends chunkBWJLMREN_cjs.DecorationPlugin {
4578
+ name = "emoji";
4579
+ version = "1.0.0";
4580
+ decorationPriority = 20;
4581
+ requiredNodes = ["Emoji", "EmojiMark"];
4582
+ constructor() {
4583
+ super();
4584
+ }
4585
+ /**
4586
+ * Plugin theme
4587
+ */
4588
+ get theme() {
4589
+ return theme13;
4590
+ }
4591
+ /**
4592
+ * Build emoji decorations by iterating the syntax tree
4593
+ */
4594
+ buildDecorations(ctx) {
4595
+ const { view: view$1, decorations } = ctx;
4596
+ const tree = language.syntaxTree(view$1.state);
4597
+ tree.iterate({
4598
+ enter: (node) => {
4599
+ const { from, to, name } = node;
4600
+ if (name !== "Emoji") {
4601
+ return;
4602
+ }
4603
+ const raw = view$1.state.sliceDoc(from, to);
4604
+ const rendered = shortcodeToEmoji(raw);
4605
+ if (!rendered) {
4606
+ return;
4607
+ }
4608
+ const cursorInNode = ctx.selectionOverlapsRange(from, to);
4609
+ if (cursorInNode) {
4610
+ decorations.push(emojiMarkDecorations["emoji-source"].range(from, to));
4611
+ return;
4612
+ }
4613
+ decorations.push(
4614
+ view.Decoration.replace({
4615
+ widget: new EmojiWidget(rendered)
4616
+ }).range(from, to)
4617
+ );
4618
+ }
4619
+ });
4620
+ }
4621
+ renderToHTML(node, children, ctx) {
4622
+ if (node.name === "EmojiMark") {
4623
+ return "";
4624
+ }
4625
+ if (node.name !== "Emoji") {
4626
+ return null;
4627
+ }
4628
+ const raw = ctx.sliceDoc(node.from, node.to);
4629
+ const rendered = shortcodeToEmoji(raw);
4630
+ if (!rendered) {
4631
+ return `<span class="cm-draftly-emoji-source">${children}</span>`;
4632
+ }
4633
+ return `<span class="cm-draftly-emoji">${rendered}</span>`;
4634
+ }
4635
+ };
4636
+ var theme13 = chunkTBVZEK2H_cjs.createTheme({
4637
+ default: {
4638
+ ".cm-draftly-emoji": {
4639
+ fontFamily: '"Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Segoe UI Symbol", sans-serif',
4640
+ fontVariantEmoji: "emoji",
4641
+ lineHeight: "1.2"
4642
+ },
4643
+ ".cm-draftly-emoji-source": {
4644
+ fontFamily: "inherit",
4645
+ lineHeight: "inherit"
4646
+ }
4647
+ }
4648
+ });
3349
4649
 
3350
4650
  // src/plugins/index.ts
3351
4651
  var essentialPlugins = [
@@ -3354,17 +4654,20 @@ var essentialPlugins = [
3354
4654
  new InlinePlugin(),
3355
4655
  new LinkPlugin(),
3356
4656
  new ListPlugin(),
4657
+ new TablePlugin(),
3357
4658
  new HTMLPlugin(),
3358
4659
  new ImagePlugin(),
3359
4660
  new MathPlugin(),
3360
4661
  new MermaidPlugin(),
3361
4662
  new CodePlugin(),
3362
4663
  new QuotePlugin(),
3363
- new HRPlugin()
4664
+ new HRPlugin(),
4665
+ new EmojiPlugin()
3364
4666
  ];
3365
4667
  var allPlugins = [...essentialPlugins];
3366
4668
 
3367
4669
  exports.CodePlugin = CodePlugin;
4670
+ exports.EmojiPlugin = EmojiPlugin;
3368
4671
  exports.HRPlugin = HRPlugin;
3369
4672
  exports.HTMLPlugin = HTMLPlugin;
3370
4673
  exports.HeadingPlugin = HeadingPlugin;
@@ -3376,7 +4679,8 @@ exports.MathPlugin = MathPlugin;
3376
4679
  exports.MermaidPlugin = MermaidPlugin;
3377
4680
  exports.ParagraphPlugin = ParagraphPlugin;
3378
4681
  exports.QuotePlugin = QuotePlugin;
4682
+ exports.TablePlugin = TablePlugin;
3379
4683
  exports.allPlugins = allPlugins;
3380
4684
  exports.essentialPlugins = essentialPlugins;
3381
- //# sourceMappingURL=chunk-2B3A3VSQ.cjs.map
3382
- //# sourceMappingURL=chunk-2B3A3VSQ.cjs.map
4685
+ //# sourceMappingURL=chunk-W5ALMXG2.cjs.map
4686
+ //# sourceMappingURL=chunk-W5ALMXG2.cjs.map