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.
- package/README.md +12 -0
- package/dist/chunk-3T55CBNZ.cjs +33 -0
- package/dist/chunk-3T55CBNZ.cjs.map +1 -0
- package/dist/chunk-5MC4T7JH.cjs +58 -0
- package/dist/chunk-5MC4T7JH.cjs.map +1 -0
- package/dist/{chunk-72ZYRGRT.cjs → chunk-BWJLMREN.cjs} +11 -9
- package/dist/chunk-BWJLMREN.cjs.map +1 -0
- package/dist/{chunk-KBQDZ5IW.cjs → chunk-CLW73JRX.cjs} +100 -75
- package/dist/chunk-CLW73JRX.cjs.map +1 -0
- package/dist/{chunk-DFQYXFOP.js → chunk-EEHILRG5.js} +26 -3
- package/dist/chunk-EEHILRG5.js.map +1 -0
- package/dist/{chunk-HPSMS2WB.js → chunk-I563H35S.js} +101 -75
- package/dist/chunk-I563H35S.js.map +1 -0
- package/dist/chunk-IAXF4SJL.js +55 -0
- package/dist/chunk-IAXF4SJL.js.map +1 -0
- package/dist/chunk-JF3WXXMJ.js +31 -0
- package/dist/chunk-JF3WXXMJ.js.map +1 -0
- package/dist/{chunk-N3WL3XPB.js → chunk-L2XSK57Y.js} +1761 -478
- package/dist/chunk-L2XSK57Y.js.map +1 -0
- package/dist/{chunk-KDEDLC3D.cjs → chunk-TBVZEK2H.cjs} +27 -2
- package/dist/chunk-TBVZEK2H.cjs.map +1 -0
- package/dist/{chunk-2B3A3VSQ.cjs → chunk-W5ALMXG2.cjs} +1808 -504
- package/dist/chunk-W5ALMXG2.cjs.map +1 -0
- package/dist/{chunk-CG4M4TC7.js → chunk-ZUI3GI3W.js} +7 -5
- package/dist/chunk-ZUI3GI3W.js.map +1 -0
- package/dist/{draftly-BLnx3uGX.d.cts → draftly-BBL-AdOl.d.cts} +5 -1
- package/dist/{draftly-BLnx3uGX.d.ts → draftly-BBL-AdOl.d.ts} +5 -1
- package/dist/editor/index.cjs +22 -14
- package/dist/editor/index.d.cts +2 -1
- package/dist/editor/index.d.ts +2 -1
- package/dist/editor/index.js +2 -2
- package/dist/index.cjs +65 -39
- package/dist/index.d.cts +6 -3
- package/dist/index.d.ts +6 -3
- package/dist/index.js +6 -4
- package/dist/lib/index.cjs +12 -0
- package/dist/lib/index.cjs.map +1 -0
- package/dist/lib/index.d.cts +16 -0
- package/dist/lib/index.d.ts +16 -0
- package/dist/lib/index.js +3 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/plugins/index.cjs +27 -17
- package/dist/plugins/index.d.cts +144 -9
- package/dist/plugins/index.d.ts +144 -9
- package/dist/plugins/index.js +5 -3
- package/dist/preview/index.cjs +16 -11
- package/dist/preview/index.d.cts +19 -4
- package/dist/preview/index.d.ts +19 -4
- package/dist/preview/index.js +3 -2
- package/package.json +8 -1
- package/src/editor/draftly.ts +1 -0
- package/src/editor/plugin.ts +5 -1
- package/src/editor/theme.ts +1 -0
- package/src/editor/utils.ts +31 -0
- package/src/index.ts +5 -4
- package/src/lib/index.ts +2 -0
- package/src/lib/input-handler.ts +45 -0
- package/src/plugins/code-plugin.theme.ts +426 -0
- package/src/plugins/code-plugin.ts +810 -561
- package/src/plugins/emoji-plugin.ts +140 -0
- package/src/plugins/index.ts +63 -57
- package/src/plugins/inline-plugin.ts +305 -291
- package/src/plugins/math-plugin.ts +12 -0
- package/src/plugins/table-plugin.ts +900 -0
- package/src/preview/context.ts +4 -1
- package/src/preview/css-generator.ts +14 -1
- package/src/preview/index.ts +9 -1
- package/src/preview/preview.ts +2 -1
- package/src/preview/renderer.ts +21 -20
- package/src/preview/syntax-theme.ts +110 -0
- package/src/preview/types.ts +14 -0
- package/dist/chunk-2B3A3VSQ.cjs.map +0 -1
- package/dist/chunk-72ZYRGRT.cjs.map +0 -1
- package/dist/chunk-CG4M4TC7.js.map +0 -1
- package/dist/chunk-DFQYXFOP.js.map +0 -1
- package/dist/chunk-HPSMS2WB.js.map +0 -1
- package/dist/chunk-KBQDZ5IW.cjs.map +0 -1
- package/dist/chunk-KDEDLC3D.cjs.map +0 -1
- package/dist/chunk-N3WL3XPB.js.map +0 -1
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import { DraftlyPlugin, DecorationPlugin } from './chunk-
|
|
2
|
-
import {
|
|
1
|
+
import { DraftlyPlugin, DecorationPlugin } from './chunk-ZUI3GI3W.js';
|
|
2
|
+
import { createWrapSelectionInputHandler } from './chunk-JF3WXXMJ.js';
|
|
3
|
+
import { PreviewRenderer } from './chunk-I563H35S.js';
|
|
4
|
+
import { createTheme, toggleMarkdownStyle } from './chunk-EEHILRG5.js';
|
|
3
5
|
import { Decoration, WidgetType } from '@codemirror/view';
|
|
4
|
-
import { syntaxTree } from '@codemirror/language';
|
|
5
|
-
import { tags, highlightCode
|
|
6
|
+
import { syntaxTree, LanguageDescription } from '@codemirror/language';
|
|
7
|
+
import { tags, highlightCode } from '@lezer/highlight';
|
|
6
8
|
import DOMPurify from 'dompurify';
|
|
7
9
|
import katex from 'katex';
|
|
8
10
|
import katexCss from 'katex/dist/katex.min.css?raw';
|
|
9
11
|
import mermaid from 'mermaid';
|
|
10
12
|
import { languages } from '@codemirror/language-data';
|
|
13
|
+
import * as emoji from 'node-emoji';
|
|
11
14
|
|
|
12
15
|
// src/plugins/paragraph-plugin.ts
|
|
13
16
|
var ParagraphPlugin = class extends DraftlyPlugin {
|
|
@@ -292,6 +295,17 @@ var InlinePlugin = class extends DecorationPlugin {
|
|
|
292
295
|
}
|
|
293
296
|
];
|
|
294
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Intercepts inline marker typing to wrap selected text.
|
|
300
|
+
*
|
|
301
|
+
* If user types inline markers while text is selected, wraps each selected
|
|
302
|
+
* range with the appropriate marker:
|
|
303
|
+
* - * _ ~ ^ -> marker + selected + marker
|
|
304
|
+
* - = -> ==selected==
|
|
305
|
+
*/
|
|
306
|
+
getExtensions() {
|
|
307
|
+
return [createWrapSelectionInputHandler({ "*": "*", _: "_", "~": "~", "^": "^", "=": "==" })];
|
|
308
|
+
}
|
|
295
309
|
/**
|
|
296
310
|
* Return markdown parser extensions for highlight syntax (==text==)
|
|
297
311
|
*/
|
|
@@ -1110,6 +1124,653 @@ var theme5 = createTheme({
|
|
|
1110
1124
|
}
|
|
1111
1125
|
}
|
|
1112
1126
|
});
|
|
1127
|
+
function parseAlignment(cell) {
|
|
1128
|
+
const trimmed = cell.trim();
|
|
1129
|
+
const left = trimmed.startsWith(":");
|
|
1130
|
+
const right = trimmed.endsWith(":");
|
|
1131
|
+
if (left && right) return "center";
|
|
1132
|
+
if (right) return "right";
|
|
1133
|
+
return "left";
|
|
1134
|
+
}
|
|
1135
|
+
function parseTableMarkdown(markdown) {
|
|
1136
|
+
const lines = markdown.split("\n").filter((l) => l.trim().length > 0);
|
|
1137
|
+
if (lines.length < 2) return null;
|
|
1138
|
+
const parseCells = (line) => {
|
|
1139
|
+
let trimmed = line.trim();
|
|
1140
|
+
if (trimmed.startsWith("|")) trimmed = trimmed.slice(1);
|
|
1141
|
+
if (trimmed.endsWith("|")) trimmed = trimmed.slice(0, -1);
|
|
1142
|
+
return trimmed.split("|").map((c) => c.trim());
|
|
1143
|
+
};
|
|
1144
|
+
const headers = parseCells(lines[0]);
|
|
1145
|
+
const delimiterCells = parseCells(lines[1]);
|
|
1146
|
+
const isDelimiter = delimiterCells.every((c) => /^:?-+:?$/.test(c.trim()));
|
|
1147
|
+
if (!isDelimiter) return null;
|
|
1148
|
+
const alignments = delimiterCells.map(parseAlignment);
|
|
1149
|
+
const rows = [];
|
|
1150
|
+
for (let i = 2; i < lines.length; i++) {
|
|
1151
|
+
rows.push(parseCells(lines[i]));
|
|
1152
|
+
}
|
|
1153
|
+
return { headers, alignments, rows };
|
|
1154
|
+
}
|
|
1155
|
+
function isRowEmpty(rowText) {
|
|
1156
|
+
const trimmed = rowText.trim();
|
|
1157
|
+
if (!trimmed.startsWith("|")) return false;
|
|
1158
|
+
let inner = trimmed;
|
|
1159
|
+
if (inner.startsWith("|")) inner = inner.slice(1);
|
|
1160
|
+
if (inner.endsWith("|")) inner = inner.slice(0, -1);
|
|
1161
|
+
return inner.split("|").every((cell) => cell.trim() === "");
|
|
1162
|
+
}
|
|
1163
|
+
async function renderCellWithPreviewRenderer(text, config) {
|
|
1164
|
+
if (!text.trim()) {
|
|
1165
|
+
return " ";
|
|
1166
|
+
}
|
|
1167
|
+
const renderer = new PreviewRenderer(
|
|
1168
|
+
text,
|
|
1169
|
+
config?.plugins || [],
|
|
1170
|
+
config?.markdown || [],
|
|
1171
|
+
config?.theme || "auto" /* AUTO */,
|
|
1172
|
+
true
|
|
1173
|
+
);
|
|
1174
|
+
const html = await renderer.render();
|
|
1175
|
+
const paragraphMatch = html.match(/^\s*<p>([\s\S]*)<\/p>\s*$/i);
|
|
1176
|
+
if (paragraphMatch && paragraphMatch[1] !== void 0) {
|
|
1177
|
+
return paragraphMatch[1];
|
|
1178
|
+
}
|
|
1179
|
+
return html;
|
|
1180
|
+
}
|
|
1181
|
+
function getColumnAlignment(alignments, index) {
|
|
1182
|
+
return alignments[index] || "left";
|
|
1183
|
+
}
|
|
1184
|
+
function getColumnCount(headers, row) {
|
|
1185
|
+
return Math.max(headers.length, row.length);
|
|
1186
|
+
}
|
|
1187
|
+
async function renderTableToHtml(parsed, config) {
|
|
1188
|
+
const { headers, alignments, rows } = parsed;
|
|
1189
|
+
let html = '<div class="cm-draftly-table-widget">';
|
|
1190
|
+
html += '<table class="cm-draftly-table">';
|
|
1191
|
+
html += "<thead><tr>";
|
|
1192
|
+
for (let i = 0; i < headers.length; i++) {
|
|
1193
|
+
const cell = headers[i] || "";
|
|
1194
|
+
const align = getColumnAlignment(alignments, i);
|
|
1195
|
+
const rendered = await renderCellWithPreviewRenderer(cell, config);
|
|
1196
|
+
html += `<th style="text-align: ${align}">${rendered}</th>`;
|
|
1197
|
+
}
|
|
1198
|
+
html += "</tr></thead>";
|
|
1199
|
+
html += "<tbody>";
|
|
1200
|
+
for (const row of rows) {
|
|
1201
|
+
html += "<tr>";
|
|
1202
|
+
const colCount = getColumnCount(headers, row);
|
|
1203
|
+
for (let i = 0; i < colCount; i++) {
|
|
1204
|
+
const align = getColumnAlignment(alignments, i);
|
|
1205
|
+
const cell = row[i] || "";
|
|
1206
|
+
const rendered = await renderCellWithPreviewRenderer(cell, config);
|
|
1207
|
+
html += `<td style="text-align: ${align}">${rendered}</td>`;
|
|
1208
|
+
}
|
|
1209
|
+
html += "</tr>";
|
|
1210
|
+
}
|
|
1211
|
+
html += "</tbody>";
|
|
1212
|
+
html += "</table></div>";
|
|
1213
|
+
return html;
|
|
1214
|
+
}
|
|
1215
|
+
var TableWidget = class extends WidgetType {
|
|
1216
|
+
constructor(tableMarkdown, from, to, config) {
|
|
1217
|
+
super();
|
|
1218
|
+
this.tableMarkdown = tableMarkdown;
|
|
1219
|
+
this.from = from;
|
|
1220
|
+
this.to = to;
|
|
1221
|
+
this.config = config;
|
|
1222
|
+
}
|
|
1223
|
+
eq(other) {
|
|
1224
|
+
return other.tableMarkdown === this.tableMarkdown && other.from === this.from && other.to === this.to && other.config === this.config;
|
|
1225
|
+
}
|
|
1226
|
+
toDOM(view) {
|
|
1227
|
+
const wrapper = document.createElement("div");
|
|
1228
|
+
wrapper.className = "cm-draftly-table-widget";
|
|
1229
|
+
const parsed = parseTableMarkdown(this.tableMarkdown);
|
|
1230
|
+
if (!parsed) {
|
|
1231
|
+
wrapper.textContent = "[Invalid table]";
|
|
1232
|
+
return wrapper;
|
|
1233
|
+
}
|
|
1234
|
+
const { headers, alignments, rows } = parsed;
|
|
1235
|
+
const table = document.createElement("table");
|
|
1236
|
+
table.className = "cm-draftly-table";
|
|
1237
|
+
const thead = document.createElement("thead");
|
|
1238
|
+
const headerRow = document.createElement("tr");
|
|
1239
|
+
headers.forEach((h, i) => {
|
|
1240
|
+
const th = document.createElement("th");
|
|
1241
|
+
th.innerHTML = " ";
|
|
1242
|
+
this.renderCellAsync(h, th);
|
|
1243
|
+
const align = alignments[i];
|
|
1244
|
+
if (align) th.style.textAlign = align;
|
|
1245
|
+
headerRow.appendChild(th);
|
|
1246
|
+
});
|
|
1247
|
+
thead.appendChild(headerRow);
|
|
1248
|
+
table.appendChild(thead);
|
|
1249
|
+
const tbody = document.createElement("tbody");
|
|
1250
|
+
rows.forEach((row) => {
|
|
1251
|
+
const tr = document.createElement("tr");
|
|
1252
|
+
const colCount = getColumnCount(headers, row);
|
|
1253
|
+
for (let i = 0; i < colCount; i++) {
|
|
1254
|
+
const td = document.createElement("td");
|
|
1255
|
+
td.innerHTML = " ";
|
|
1256
|
+
this.renderCellAsync(row[i] || "", td);
|
|
1257
|
+
const align = getColumnAlignment(alignments, i);
|
|
1258
|
+
td.style.textAlign = align;
|
|
1259
|
+
tr.appendChild(td);
|
|
1260
|
+
}
|
|
1261
|
+
tbody.appendChild(tr);
|
|
1262
|
+
});
|
|
1263
|
+
table.appendChild(tbody);
|
|
1264
|
+
wrapper.appendChild(table);
|
|
1265
|
+
wrapper.addEventListener("click", (e) => {
|
|
1266
|
+
e.preventDefault();
|
|
1267
|
+
e.stopPropagation();
|
|
1268
|
+
view.dispatch({
|
|
1269
|
+
selection: { anchor: this.from },
|
|
1270
|
+
scrollIntoView: true
|
|
1271
|
+
});
|
|
1272
|
+
view.focus();
|
|
1273
|
+
});
|
|
1274
|
+
return wrapper;
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Render cell content asynchronously using PreviewRenderer
|
|
1278
|
+
*/
|
|
1279
|
+
async renderCellAsync(text, element) {
|
|
1280
|
+
if (!text.trim()) {
|
|
1281
|
+
element.innerHTML = " ";
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
try {
|
|
1285
|
+
element.innerHTML = await renderCellWithPreviewRenderer(text, this.config);
|
|
1286
|
+
} catch (error) {
|
|
1287
|
+
console.error("Failed to render table cell:", error);
|
|
1288
|
+
element.textContent = text;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
ignoreEvent(event) {
|
|
1292
|
+
return event.type !== "click";
|
|
1293
|
+
}
|
|
1294
|
+
};
|
|
1295
|
+
var tableMarkDecorations = {
|
|
1296
|
+
"table-line": Decoration.line({ class: "cm-draftly-table-line" }),
|
|
1297
|
+
"table-line-start": Decoration.line({ class: "cm-draftly-table-line-start" }),
|
|
1298
|
+
"table-line-end": Decoration.line({ class: "cm-draftly-table-line-end" }),
|
|
1299
|
+
"table-delimiter": Decoration.line({ class: "cm-draftly-table-delimiter-line" }),
|
|
1300
|
+
"table-rendered": Decoration.line({ class: "cm-draftly-table-rendered" }),
|
|
1301
|
+
// "table-hidden": Decoration.mark({ class: "cm-draftly-table-hidden" }),
|
|
1302
|
+
"table-hidden": Decoration.replace({})
|
|
1303
|
+
};
|
|
1304
|
+
var TablePlugin = class extends DecorationPlugin {
|
|
1305
|
+
name = "table";
|
|
1306
|
+
version = "1.0.0";
|
|
1307
|
+
decorationPriority = 20;
|
|
1308
|
+
requiredNodes = ["Table", "TableHeader", "TableDelimiter", "TableRow", "TableCell"];
|
|
1309
|
+
/** Configuration stored from onRegister */
|
|
1310
|
+
draftlyConfig;
|
|
1311
|
+
onRegister(context) {
|
|
1312
|
+
super.onRegister(context);
|
|
1313
|
+
this.draftlyConfig = context.config;
|
|
1314
|
+
}
|
|
1315
|
+
get theme() {
|
|
1316
|
+
return theme6;
|
|
1317
|
+
}
|
|
1318
|
+
// ============================================
|
|
1319
|
+
// Keymaps
|
|
1320
|
+
// ============================================
|
|
1321
|
+
getKeymap() {
|
|
1322
|
+
return [
|
|
1323
|
+
{
|
|
1324
|
+
key: "Mod-Shift-t",
|
|
1325
|
+
run: (view) => this.insertTable(view),
|
|
1326
|
+
preventDefault: true
|
|
1327
|
+
},
|
|
1328
|
+
{
|
|
1329
|
+
key: "Mod-Enter",
|
|
1330
|
+
run: (view) => this.addRow(view),
|
|
1331
|
+
preventDefault: true
|
|
1332
|
+
},
|
|
1333
|
+
{
|
|
1334
|
+
key: "Mod-Shift-Enter",
|
|
1335
|
+
run: (view) => this.addColumn(view),
|
|
1336
|
+
preventDefault: true
|
|
1337
|
+
},
|
|
1338
|
+
{
|
|
1339
|
+
key: "Enter",
|
|
1340
|
+
run: (view) => this.handleEnter(view)
|
|
1341
|
+
},
|
|
1342
|
+
{
|
|
1343
|
+
key: "Tab",
|
|
1344
|
+
run: (view) => this.handleTab(view, false)
|
|
1345
|
+
},
|
|
1346
|
+
{
|
|
1347
|
+
key: "Shift-Tab",
|
|
1348
|
+
run: (view) => this.handleTab(view, true)
|
|
1349
|
+
}
|
|
1350
|
+
];
|
|
1351
|
+
}
|
|
1352
|
+
// ============================================
|
|
1353
|
+
// Decorations
|
|
1354
|
+
// ============================================
|
|
1355
|
+
buildDecorations(ctx) {
|
|
1356
|
+
const { view, decorations } = ctx;
|
|
1357
|
+
const tree = syntaxTree(view.state);
|
|
1358
|
+
tree.iterate({
|
|
1359
|
+
enter: (node) => {
|
|
1360
|
+
if (node.name !== "Table") return;
|
|
1361
|
+
const { from, to } = node;
|
|
1362
|
+
const nodeLineStart = view.state.doc.lineAt(from);
|
|
1363
|
+
const nodeLineEnd = view.state.doc.lineAt(to);
|
|
1364
|
+
const cursorInRange = ctx.selectionOverlapsRange(nodeLineStart.from, nodeLineEnd.to);
|
|
1365
|
+
if (cursorInRange) {
|
|
1366
|
+
for (let i = nodeLineStart.number; i <= nodeLineEnd.number; i++) {
|
|
1367
|
+
const line = view.state.doc.line(i);
|
|
1368
|
+
decorations.push(tableMarkDecorations["table-line"].range(line.from));
|
|
1369
|
+
if (i === nodeLineStart.number) {
|
|
1370
|
+
decorations.push(tableMarkDecorations["table-line-start"].range(line.from));
|
|
1371
|
+
}
|
|
1372
|
+
if (i === nodeLineEnd.number) {
|
|
1373
|
+
decorations.push(tableMarkDecorations["table-line-end"].range(line.from));
|
|
1374
|
+
}
|
|
1375
|
+
if (i === nodeLineStart.number + 1) {
|
|
1376
|
+
decorations.push(tableMarkDecorations["table-delimiter"].range(line.from));
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
} else {
|
|
1380
|
+
const tableContent = view.state.sliceDoc(from, to);
|
|
1381
|
+
for (let i = nodeLineStart.number; i <= nodeLineEnd.number; i++) {
|
|
1382
|
+
const line = view.state.doc.line(i);
|
|
1383
|
+
decorations.push(tableMarkDecorations["table-rendered"].range(line.from));
|
|
1384
|
+
decorations.push(tableMarkDecorations["table-hidden"].range(line.from, line.to));
|
|
1385
|
+
}
|
|
1386
|
+
decorations.push(
|
|
1387
|
+
Decoration.widget({
|
|
1388
|
+
widget: new TableWidget(tableContent, from, to, this.draftlyConfig),
|
|
1389
|
+
side: 1,
|
|
1390
|
+
block: false
|
|
1391
|
+
}).range(to)
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
// ============================================
|
|
1398
|
+
// Keymap Handlers
|
|
1399
|
+
// ============================================
|
|
1400
|
+
/**
|
|
1401
|
+
* Insert a new 3×3 table at cursor position
|
|
1402
|
+
*/
|
|
1403
|
+
insertTable(view) {
|
|
1404
|
+
const { state } = view;
|
|
1405
|
+
const cursor = state.selection.main.head;
|
|
1406
|
+
const line = state.doc.lineAt(cursor);
|
|
1407
|
+
const insertPos = line.text.trim() ? line.to : line.from;
|
|
1408
|
+
const template = [
|
|
1409
|
+
"| Header 1 | Header 2 | Header 3 |",
|
|
1410
|
+
"| -------- | -------- | -------- |",
|
|
1411
|
+
"| | | |"
|
|
1412
|
+
].join("\n");
|
|
1413
|
+
const prefix = line.text.trim() ? "\n" : "";
|
|
1414
|
+
const suffix = "\n";
|
|
1415
|
+
view.dispatch({
|
|
1416
|
+
changes: {
|
|
1417
|
+
from: insertPos,
|
|
1418
|
+
insert: prefix + template + suffix
|
|
1419
|
+
},
|
|
1420
|
+
selection: {
|
|
1421
|
+
anchor: insertPos + prefix.length + 2
|
|
1422
|
+
// Position cursor in first header cell
|
|
1423
|
+
}
|
|
1424
|
+
});
|
|
1425
|
+
return true;
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Add a new row below the current row (Mod-Enter)
|
|
1429
|
+
*/
|
|
1430
|
+
addRow(view) {
|
|
1431
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
1432
|
+
if (!tableInfo) return false;
|
|
1433
|
+
const { state } = view;
|
|
1434
|
+
const cursor = state.selection.main.head;
|
|
1435
|
+
const currentLine = state.doc.lineAt(cursor);
|
|
1436
|
+
const parsed = parseTableMarkdown(state.sliceDoc(tableInfo.from, tableInfo.to));
|
|
1437
|
+
if (!parsed) return false;
|
|
1438
|
+
const colCount = parsed.headers.length;
|
|
1439
|
+
const emptyRow = "| " + Array.from({ length: colCount }, () => " ").join(" | ") + " |";
|
|
1440
|
+
view.dispatch({
|
|
1441
|
+
changes: {
|
|
1442
|
+
from: currentLine.to,
|
|
1443
|
+
insert: "\n" + emptyRow
|
|
1444
|
+
},
|
|
1445
|
+
selection: {
|
|
1446
|
+
anchor: currentLine.to + 3
|
|
1447
|
+
// Position in first cell of new row
|
|
1448
|
+
}
|
|
1449
|
+
});
|
|
1450
|
+
return true;
|
|
1451
|
+
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Add a new column after the current column (Mod-Shift-Enter)
|
|
1454
|
+
*/
|
|
1455
|
+
addColumn(view) {
|
|
1456
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
1457
|
+
if (!tableInfo) return false;
|
|
1458
|
+
const { state } = view;
|
|
1459
|
+
const cursor = state.selection.main.head;
|
|
1460
|
+
const currentLine = state.doc.lineAt(cursor);
|
|
1461
|
+
const lineText = currentLine.text;
|
|
1462
|
+
const cursorInLine = cursor - currentLine.from;
|
|
1463
|
+
let colIndex = -1;
|
|
1464
|
+
for (let i = 0; i < cursorInLine; i++) {
|
|
1465
|
+
if (lineText[i] === "|") colIndex++;
|
|
1466
|
+
}
|
|
1467
|
+
colIndex = Math.max(0, colIndex);
|
|
1468
|
+
const tableText = state.sliceDoc(tableInfo.from, tableInfo.to);
|
|
1469
|
+
const lines = tableText.split("\n");
|
|
1470
|
+
const newLines = lines.map((line, lineIdx) => {
|
|
1471
|
+
const cells = this.splitLineToCells(line);
|
|
1472
|
+
const insertAfter = Math.min(colIndex, cells.length - 1);
|
|
1473
|
+
if (lineIdx === 1) {
|
|
1474
|
+
cells.splice(insertAfter + 1, 0, " -------- ");
|
|
1475
|
+
} else {
|
|
1476
|
+
cells.splice(insertAfter + 1, 0, " ");
|
|
1477
|
+
}
|
|
1478
|
+
return "|" + cells.join("|") + "|";
|
|
1479
|
+
});
|
|
1480
|
+
view.dispatch({
|
|
1481
|
+
changes: {
|
|
1482
|
+
from: tableInfo.from,
|
|
1483
|
+
to: tableInfo.to,
|
|
1484
|
+
insert: newLines.join("\n")
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
return true;
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* Handle Enter key inside a table.
|
|
1491
|
+
* - Last cell of last row: create a new row
|
|
1492
|
+
* - Empty last row: remove it and move cursor after table
|
|
1493
|
+
*/
|
|
1494
|
+
handleEnter(view) {
|
|
1495
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
1496
|
+
if (!tableInfo) return false;
|
|
1497
|
+
const { state } = view;
|
|
1498
|
+
const cursor = state.selection.main.head;
|
|
1499
|
+
const cursorLine = state.doc.lineAt(cursor);
|
|
1500
|
+
const tableEndLine = state.doc.lineAt(tableInfo.to);
|
|
1501
|
+
if (cursorLine.number !== tableEndLine.number) return false;
|
|
1502
|
+
const lineText = cursorLine.text;
|
|
1503
|
+
const cursorOffset = cursor - cursorLine.from;
|
|
1504
|
+
const pipes = [];
|
|
1505
|
+
for (let i = 0; i < lineText.length; i++) {
|
|
1506
|
+
if (lineText[i] === "|") pipes.push(i);
|
|
1507
|
+
}
|
|
1508
|
+
if (pipes.length < 2) return false;
|
|
1509
|
+
const lastCellStart = pipes[pipes.length - 2];
|
|
1510
|
+
if (cursorOffset < lastCellStart) return false;
|
|
1511
|
+
if (isRowEmpty(lineText)) {
|
|
1512
|
+
const removeFrom = cursorLine.from - 1;
|
|
1513
|
+
const removeTo = cursorLine.to;
|
|
1514
|
+
view.dispatch({
|
|
1515
|
+
changes: { from: Math.max(0, removeFrom), to: removeTo },
|
|
1516
|
+
selection: {
|
|
1517
|
+
anchor: Math.min(Math.max(0, removeFrom) + 1, view.state.doc.length)
|
|
1518
|
+
}
|
|
1519
|
+
});
|
|
1520
|
+
return true;
|
|
1521
|
+
}
|
|
1522
|
+
const parsed = parseTableMarkdown(state.sliceDoc(tableInfo.from, tableInfo.to));
|
|
1523
|
+
if (!parsed) return false;
|
|
1524
|
+
const colCount = parsed.headers.length;
|
|
1525
|
+
const emptyRow = "| " + Array.from({ length: colCount }, () => " ").join(" | ") + " |";
|
|
1526
|
+
view.dispatch({
|
|
1527
|
+
changes: {
|
|
1528
|
+
from: cursorLine.to,
|
|
1529
|
+
insert: "\n" + emptyRow
|
|
1530
|
+
},
|
|
1531
|
+
selection: {
|
|
1532
|
+
anchor: cursorLine.to + 3
|
|
1533
|
+
// Position in first cell of new row
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
1536
|
+
return true;
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Handle Tab key inside a table — move to next/previous cell
|
|
1540
|
+
*/
|
|
1541
|
+
handleTab(view, backwards) {
|
|
1542
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
1543
|
+
if (!tableInfo) return false;
|
|
1544
|
+
const { state } = view;
|
|
1545
|
+
const cursor = state.selection.main.head;
|
|
1546
|
+
const tableText = state.sliceDoc(tableInfo.from, tableInfo.to);
|
|
1547
|
+
const lines = tableText.split("\n");
|
|
1548
|
+
const cellPositions = [];
|
|
1549
|
+
for (let li = 0; li < lines.length; li++) {
|
|
1550
|
+
if (li === 1) continue;
|
|
1551
|
+
const line = lines[li];
|
|
1552
|
+
const lineFrom = tableInfo.from + lines.slice(0, li).reduce((sum, l) => sum + l.length + 1, 0);
|
|
1553
|
+
const pipes = [];
|
|
1554
|
+
for (let i = 0; i < line.length; i++) {
|
|
1555
|
+
if (line[i] === "|") pipes.push(i);
|
|
1556
|
+
}
|
|
1557
|
+
for (let p = 0; p < pipes.length - 1; p++) {
|
|
1558
|
+
const cellStart = pipes[p] + 1;
|
|
1559
|
+
const cellEnd = pipes[p + 1];
|
|
1560
|
+
cellPositions.push({
|
|
1561
|
+
lineFrom,
|
|
1562
|
+
start: cellStart,
|
|
1563
|
+
end: cellEnd
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
let currentCellIdx = -1;
|
|
1568
|
+
for (let i = 0; i < cellPositions.length; i++) {
|
|
1569
|
+
const cell = cellPositions[i];
|
|
1570
|
+
const absStart = cell.lineFrom + cell.start;
|
|
1571
|
+
const absEnd = cell.lineFrom + cell.end;
|
|
1572
|
+
if (cursor >= absStart && cursor <= absEnd) {
|
|
1573
|
+
currentCellIdx = i;
|
|
1574
|
+
break;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
if (currentCellIdx === -1) return false;
|
|
1578
|
+
const nextIdx = backwards ? currentCellIdx - 1 : currentCellIdx + 1;
|
|
1579
|
+
if (nextIdx < 0 || nextIdx >= cellPositions.length) return false;
|
|
1580
|
+
const nextCell = cellPositions[nextIdx];
|
|
1581
|
+
const cellText = state.sliceDoc(nextCell.lineFrom + nextCell.start, nextCell.lineFrom + nextCell.end);
|
|
1582
|
+
const trimStart = cellText.length - cellText.trimStart().length;
|
|
1583
|
+
const trimEnd = cellText.length - cellText.trimEnd().length;
|
|
1584
|
+
const selectFrom = nextCell.lineFrom + nextCell.start + (trimStart > 0 ? 1 : 0);
|
|
1585
|
+
const selectTo = nextCell.lineFrom + nextCell.end - (trimEnd > 0 ? 1 : 0);
|
|
1586
|
+
view.dispatch({
|
|
1587
|
+
selection: {
|
|
1588
|
+
anchor: selectFrom,
|
|
1589
|
+
head: selectTo
|
|
1590
|
+
},
|
|
1591
|
+
scrollIntoView: true
|
|
1592
|
+
});
|
|
1593
|
+
return true;
|
|
1594
|
+
}
|
|
1595
|
+
// ============================================
|
|
1596
|
+
// Helpers
|
|
1597
|
+
// ============================================
|
|
1598
|
+
/**
|
|
1599
|
+
* Find the Table node at the cursor position
|
|
1600
|
+
*/
|
|
1601
|
+
getTableAtCursor(view) {
|
|
1602
|
+
const tree = syntaxTree(view.state);
|
|
1603
|
+
const cursor = view.state.selection.main.head;
|
|
1604
|
+
let result = null;
|
|
1605
|
+
tree.iterate({
|
|
1606
|
+
enter: (node) => {
|
|
1607
|
+
if (node.name === "Table" && cursor >= node.from && cursor <= node.to) {
|
|
1608
|
+
result = { from: node.from, to: node.to };
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
});
|
|
1612
|
+
return result;
|
|
1613
|
+
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Split a table line into cells (keeping the whitespace around content)
|
|
1616
|
+
*/
|
|
1617
|
+
splitLineToCells(line) {
|
|
1618
|
+
let trimmed = line.trim();
|
|
1619
|
+
if (trimmed.startsWith("|")) trimmed = trimmed.slice(1);
|
|
1620
|
+
if (trimmed.endsWith("|")) trimmed = trimmed.slice(0, -1);
|
|
1621
|
+
return trimmed.split("|");
|
|
1622
|
+
}
|
|
1623
|
+
// ============================================
|
|
1624
|
+
// Preview Rendering
|
|
1625
|
+
// ============================================
|
|
1626
|
+
async renderToHTML(node, _children, _ctx) {
|
|
1627
|
+
if (node.name === "Table") {
|
|
1628
|
+
const content = _ctx.sliceDoc(node.from, node.to);
|
|
1629
|
+
const parsed = parseTableMarkdown(content);
|
|
1630
|
+
if (!parsed) return null;
|
|
1631
|
+
return await renderTableToHtml(parsed, this.draftlyConfig);
|
|
1632
|
+
}
|
|
1633
|
+
if (node.name === "TableHeader" || node.name === "TableDelimiter" || node.name === "TableRow" || node.name === "TableCell") {
|
|
1634
|
+
return "";
|
|
1635
|
+
}
|
|
1636
|
+
return null;
|
|
1637
|
+
}
|
|
1638
|
+
};
|
|
1639
|
+
var theme6 = createTheme({
|
|
1640
|
+
default: {
|
|
1641
|
+
// Raw table lines — monospace when cursor is inside
|
|
1642
|
+
".cm-draftly-table-line": {
|
|
1643
|
+
"--radius": "0.375rem",
|
|
1644
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
1645
|
+
fontSize: "0.9rem",
|
|
1646
|
+
backgroundColor: "rgba(0, 0, 0, 0.02)",
|
|
1647
|
+
padding: "0 0.75rem !important",
|
|
1648
|
+
lineHeight: "1.6",
|
|
1649
|
+
borderLeft: "1px solid var(--color-border, #e2e8f0)",
|
|
1650
|
+
borderRight: "1px solid var(--color-border, #e2e8f0)"
|
|
1651
|
+
},
|
|
1652
|
+
".cm-draftly-table-line-start": {
|
|
1653
|
+
borderTopLeftRadius: "var(--radius)",
|
|
1654
|
+
borderTopRightRadius: "var(--radius)",
|
|
1655
|
+
borderTop: "1px solid var(--color-border, #e2e8f0)"
|
|
1656
|
+
},
|
|
1657
|
+
".cm-draftly-table-line-end": {
|
|
1658
|
+
borderBottomLeftRadius: "var(--radius)",
|
|
1659
|
+
borderBottomRightRadius: "var(--radius)",
|
|
1660
|
+
borderBottom: "1px solid var(--color-border, #e2e8f0)"
|
|
1661
|
+
},
|
|
1662
|
+
".cm-draftly-table-delimiter-line": {
|
|
1663
|
+
opacity: "0.5"
|
|
1664
|
+
},
|
|
1665
|
+
// Hidden table text (when cursor is not in range)
|
|
1666
|
+
".cm-draftly-table-hidden": {
|
|
1667
|
+
display: "none"
|
|
1668
|
+
},
|
|
1669
|
+
// Line decoration for rendered state — hide line breaks
|
|
1670
|
+
".cm-draftly-table-rendered": {
|
|
1671
|
+
padding: "0 !important"
|
|
1672
|
+
},
|
|
1673
|
+
".cm-draftly-table-rendered br": {
|
|
1674
|
+
display: "none"
|
|
1675
|
+
},
|
|
1676
|
+
// Rendered table widget container
|
|
1677
|
+
".cm-draftly-table-widget": {
|
|
1678
|
+
cursor: "pointer",
|
|
1679
|
+
overflow: "auto",
|
|
1680
|
+
padding: "0.5rem 0"
|
|
1681
|
+
},
|
|
1682
|
+
// Table element
|
|
1683
|
+
".cm-draftly-table": {
|
|
1684
|
+
width: "100%",
|
|
1685
|
+
borderCollapse: "separate",
|
|
1686
|
+
borderSpacing: "0",
|
|
1687
|
+
borderRadius: "0.5rem",
|
|
1688
|
+
overflow: "hidden",
|
|
1689
|
+
border: "1px solid var(--color-border, #e2e8f0)",
|
|
1690
|
+
fontFamily: "var(--font-sans, sans-serif)",
|
|
1691
|
+
fontSize: "0.9375rem",
|
|
1692
|
+
lineHeight: "1.5"
|
|
1693
|
+
},
|
|
1694
|
+
// Table header
|
|
1695
|
+
".cm-draftly-table thead th": {
|
|
1696
|
+
padding: "0rem 0.875rem",
|
|
1697
|
+
fontWeight: "600",
|
|
1698
|
+
borderBottom: "2px solid var(--color-border, #e2e8f0)",
|
|
1699
|
+
backgroundColor: "rgba(0, 0, 0, 0.03)"
|
|
1700
|
+
},
|
|
1701
|
+
// Table cells
|
|
1702
|
+
".cm-draftly-table td": {
|
|
1703
|
+
padding: "0rem 0.875rem",
|
|
1704
|
+
borderBottom: "1px solid var(--color-border, #e2e8f0)",
|
|
1705
|
+
borderRight: "1px solid var(--color-border, #e2e8f0)"
|
|
1706
|
+
},
|
|
1707
|
+
// Remove right border on last cell
|
|
1708
|
+
".cm-draftly-table td:last-child, .cm-draftly-table th:last-child": {
|
|
1709
|
+
borderRight: "none"
|
|
1710
|
+
},
|
|
1711
|
+
// Remove bottom border on last row
|
|
1712
|
+
".cm-draftly-table tbody tr:last-child td": {
|
|
1713
|
+
borderBottom: "none"
|
|
1714
|
+
},
|
|
1715
|
+
// Alternate row colors
|
|
1716
|
+
".cm-draftly-table tbody tr:nth-child(even)": {
|
|
1717
|
+
backgroundColor: "rgba(0, 0, 0, 0.02)"
|
|
1718
|
+
},
|
|
1719
|
+
// Header cells right border
|
|
1720
|
+
".cm-draftly-table thead th:not(:last-child)": {
|
|
1721
|
+
borderRight: "1px solid var(--color-border, #e2e8f0)"
|
|
1722
|
+
},
|
|
1723
|
+
// Hover effect on rows
|
|
1724
|
+
".cm-draftly-table tbody tr:hover": {
|
|
1725
|
+
backgroundColor: "rgba(0, 0, 0, 0.04)"
|
|
1726
|
+
},
|
|
1727
|
+
// Inline code in table cells
|
|
1728
|
+
".cm-draftly-table-inline-code": {
|
|
1729
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
1730
|
+
fontSize: "0.85em",
|
|
1731
|
+
padding: "0.1em 0.35em",
|
|
1732
|
+
borderRadius: "0.25rem",
|
|
1733
|
+
backgroundColor: "rgba(0, 0, 0, 0.06)"
|
|
1734
|
+
},
|
|
1735
|
+
// Links in table cells
|
|
1736
|
+
".cm-draftly-table-link": {
|
|
1737
|
+
color: "var(--color-link, #0969da)",
|
|
1738
|
+
textDecoration: "none"
|
|
1739
|
+
},
|
|
1740
|
+
".cm-draftly-table-link:hover": {
|
|
1741
|
+
textDecoration: "underline"
|
|
1742
|
+
},
|
|
1743
|
+
// Math in table cells
|
|
1744
|
+
".cm-draftly-table-math": {
|
|
1745
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
1746
|
+
fontSize: "0.9em",
|
|
1747
|
+
color: "#6a737d"
|
|
1748
|
+
}
|
|
1749
|
+
},
|
|
1750
|
+
dark: {
|
|
1751
|
+
".cm-draftly-table-line": {
|
|
1752
|
+
backgroundColor: "rgba(255, 255, 255, 0.03)"
|
|
1753
|
+
},
|
|
1754
|
+
".cm-draftly-table thead th": {
|
|
1755
|
+
backgroundColor: "rgba(255, 255, 255, 0.05)"
|
|
1756
|
+
},
|
|
1757
|
+
".cm-draftly-table tbody tr:nth-child(even)": {
|
|
1758
|
+
backgroundColor: "rgba(255, 255, 255, 0.02)"
|
|
1759
|
+
},
|
|
1760
|
+
".cm-draftly-table tbody tr:hover": {
|
|
1761
|
+
backgroundColor: "rgba(255, 255, 255, 0.05)"
|
|
1762
|
+
},
|
|
1763
|
+
".cm-draftly-table-inline-code": {
|
|
1764
|
+
backgroundColor: "rgba(255, 255, 255, 0.08)"
|
|
1765
|
+
},
|
|
1766
|
+
".cm-draftly-table-link": {
|
|
1767
|
+
color: "var(--color-link, #58a6ff)"
|
|
1768
|
+
},
|
|
1769
|
+
".cm-draftly-table-math": {
|
|
1770
|
+
color: "#8b949e"
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
});
|
|
1113
1774
|
var htmlMarkDecorations = {
|
|
1114
1775
|
"html-tag": Decoration.mark({ class: "cm-draftly-html-tag" }),
|
|
1115
1776
|
"html-comment": Decoration.mark({ class: "cm-draftly-html-comment" })
|
|
@@ -1176,7 +1837,7 @@ var HTMLPlugin = class extends DecorationPlugin {
|
|
|
1176
1837
|
* Plugin theme
|
|
1177
1838
|
*/
|
|
1178
1839
|
get theme() {
|
|
1179
|
-
return
|
|
1840
|
+
return theme7;
|
|
1180
1841
|
}
|
|
1181
1842
|
buildDecorations(ctx) {
|
|
1182
1843
|
const { view, decorations } = ctx;
|
|
@@ -1316,7 +1977,7 @@ var HTMLPlugin = class extends DecorationPlugin {
|
|
|
1316
1977
|
}
|
|
1317
1978
|
}
|
|
1318
1979
|
};
|
|
1319
|
-
var
|
|
1980
|
+
var theme7 = createTheme({
|
|
1320
1981
|
default: {
|
|
1321
1982
|
".cm-draftly-html-tag": {
|
|
1322
1983
|
color: "#6a737d",
|
|
@@ -1446,7 +2107,7 @@ var ImagePlugin = class extends DecorationPlugin {
|
|
|
1446
2107
|
* Plugin theme
|
|
1447
2108
|
*/
|
|
1448
2109
|
get theme() {
|
|
1449
|
-
return
|
|
2110
|
+
return theme8;
|
|
1450
2111
|
}
|
|
1451
2112
|
/**
|
|
1452
2113
|
* Keyboard shortcuts for image formatting
|
|
@@ -1601,7 +2262,7 @@ var ImagePlugin = class extends DecorationPlugin {
|
|
|
1601
2262
|
return html;
|
|
1602
2263
|
}
|
|
1603
2264
|
};
|
|
1604
|
-
var
|
|
2265
|
+
var theme8 = createTheme({
|
|
1605
2266
|
default: {
|
|
1606
2267
|
".cm-draftly-image-block br": {
|
|
1607
2268
|
display: "none"
|
|
@@ -1850,7 +2511,16 @@ var MathPlugin = class extends DecorationPlugin {
|
|
|
1850
2511
|
* Plugin theme
|
|
1851
2512
|
*/
|
|
1852
2513
|
get theme() {
|
|
1853
|
-
return
|
|
2514
|
+
return theme9;
|
|
2515
|
+
}
|
|
2516
|
+
/**
|
|
2517
|
+
* Intercepts dollar typing to wrap selected text as inline math.
|
|
2518
|
+
*
|
|
2519
|
+
* If user types '$' while text is selected, wraps each selected range
|
|
2520
|
+
* with single dollars (selected -> $selected$).
|
|
2521
|
+
*/
|
|
2522
|
+
getExtensions() {
|
|
2523
|
+
return [createWrapSelectionInputHandler({ "$": "$" })];
|
|
1854
2524
|
}
|
|
1855
2525
|
/**
|
|
1856
2526
|
* Return markdown parser extensions for math syntax
|
|
@@ -1958,7 +2628,7 @@ var MathPlugin = class extends DecorationPlugin {
|
|
|
1958
2628
|
return null;
|
|
1959
2629
|
}
|
|
1960
2630
|
};
|
|
1961
|
-
var
|
|
2631
|
+
var theme9 = createTheme({
|
|
1962
2632
|
default: {
|
|
1963
2633
|
".cm-draftly-math-block": {
|
|
1964
2634
|
fontFamily: "var(--font-jetbrains-mono, monospace)"
|
|
@@ -2157,7 +2827,7 @@ var MermaidPlugin = class extends DecorationPlugin {
|
|
|
2157
2827
|
* Plugin theme
|
|
2158
2828
|
*/
|
|
2159
2829
|
get theme() {
|
|
2160
|
-
return
|
|
2830
|
+
return theme10;
|
|
2161
2831
|
}
|
|
2162
2832
|
/**
|
|
2163
2833
|
* Return markdown parser extensions for mermaid syntax
|
|
@@ -2265,7 +2935,7 @@ var MermaidPlugin = class extends DecorationPlugin {
|
|
|
2265
2935
|
return null;
|
|
2266
2936
|
}
|
|
2267
2937
|
};
|
|
2268
|
-
var
|
|
2938
|
+
var theme10 = createTheme({
|
|
2269
2939
|
default: {
|
|
2270
2940
|
// Raw mermaid block lines (monospace)
|
|
2271
2941
|
".cm-draftly-mermaid-block:not(.cm-draftly-mermaid-block-rendered)": {
|
|
@@ -2369,22 +3039,395 @@ var theme9 = createTheme({
|
|
|
2369
3039
|
}
|
|
2370
3040
|
}
|
|
2371
3041
|
});
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
var
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
3042
|
+
|
|
3043
|
+
// src/plugins/code-plugin.theme.ts
|
|
3044
|
+
var codePluginTheme = createTheme({
|
|
3045
|
+
default: {
|
|
3046
|
+
// Inline code
|
|
3047
|
+
".cm-draftly-code-inline": {
|
|
3048
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
3049
|
+
fontSize: "0.9rem",
|
|
3050
|
+
backgroundColor: "rgba(0, 0, 0, 0.05)",
|
|
3051
|
+
padding: "0.1rem 0.25rem",
|
|
3052
|
+
border: "1px solid var(--color-border)",
|
|
3053
|
+
borderRadius: "3px"
|
|
3054
|
+
},
|
|
3055
|
+
// Fenced code block lines
|
|
3056
|
+
".cm-draftly-code-block-line": {
|
|
3057
|
+
"--radius": "0.375rem",
|
|
3058
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
3059
|
+
fontSize: "0.9rem",
|
|
3060
|
+
backgroundColor: "rgba(0, 0, 0, 0.03)",
|
|
3061
|
+
padding: "0 1rem !important",
|
|
3062
|
+
lineHeight: "1.5",
|
|
3063
|
+
borderLeft: "1px solid var(--color-border)",
|
|
3064
|
+
borderRight: "1px solid var(--color-border)"
|
|
3065
|
+
},
|
|
3066
|
+
// First line of code block
|
|
3067
|
+
".cm-draftly-code-block-line-start": {
|
|
3068
|
+
borderTopLeftRadius: "var(--radius)",
|
|
3069
|
+
borderTopRightRadius: "var(--radius)",
|
|
3070
|
+
position: "relative",
|
|
3071
|
+
overflow: "hidden",
|
|
3072
|
+
borderTop: "1px solid var(--color-border)",
|
|
3073
|
+
paddingBottom: "0.5rem !important"
|
|
3074
|
+
},
|
|
3075
|
+
// Remove top radius when header is present
|
|
3076
|
+
".cm-draftly-code-block-has-header": {
|
|
3077
|
+
padding: "0 !important",
|
|
3078
|
+
paddingBottom: "0.5rem !important"
|
|
3079
|
+
},
|
|
3080
|
+
// Code block header widget
|
|
3081
|
+
".cm-draftly-code-header": {
|
|
3082
|
+
display: "flex",
|
|
3083
|
+
justifyContent: "space-between",
|
|
3084
|
+
alignItems: "center",
|
|
3085
|
+
padding: "0.25rem 1rem",
|
|
3086
|
+
backgroundColor: "rgba(0, 0, 0, 0.06)",
|
|
3087
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
3088
|
+
fontSize: "0.85rem",
|
|
3089
|
+
".cm-draftly-code-header-left": {
|
|
3090
|
+
display: "flex",
|
|
3091
|
+
alignItems: "center",
|
|
3092
|
+
gap: "0.5rem",
|
|
3093
|
+
".cm-draftly-code-header-title": {
|
|
3094
|
+
color: "var(--color-text, inherit)",
|
|
3095
|
+
fontWeight: "500"
|
|
3096
|
+
},
|
|
3097
|
+
".cm-draftly-code-header-lang": {
|
|
3098
|
+
color: "#6a737d",
|
|
3099
|
+
opacity: "0.8"
|
|
3100
|
+
}
|
|
3101
|
+
},
|
|
3102
|
+
".cm-draftly-code-header-right": {
|
|
3103
|
+
display: "flex",
|
|
3104
|
+
alignItems: "center",
|
|
3105
|
+
gap: "0.5rem",
|
|
3106
|
+
".cm-draftly-code-copy-btn": {
|
|
3107
|
+
display: "flex",
|
|
3108
|
+
alignItems: "center",
|
|
3109
|
+
justifyContent: "center",
|
|
3110
|
+
padding: "0.25rem",
|
|
3111
|
+
backgroundColor: "transparent",
|
|
3112
|
+
border: "none",
|
|
3113
|
+
borderRadius: "4px",
|
|
3114
|
+
cursor: "pointer",
|
|
3115
|
+
color: "#6a737d",
|
|
3116
|
+
transition: "color 0.2s, background-color 0.2s",
|
|
3117
|
+
"&:hover": {
|
|
3118
|
+
backgroundColor: "rgba(0, 0, 0, 0.1)",
|
|
3119
|
+
color: "var(--color-text, inherit)"
|
|
3120
|
+
},
|
|
3121
|
+
"&.copied": {
|
|
3122
|
+
color: "#22c55e"
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
},
|
|
3127
|
+
// Caption (below code block)
|
|
3128
|
+
".cm-draftly-code-block-has-caption": {
|
|
3129
|
+
padding: "0 !important",
|
|
3130
|
+
paddingTop: "0.5rem !important"
|
|
3131
|
+
},
|
|
3132
|
+
".cm-draftly-code-caption": {
|
|
3133
|
+
textAlign: "center",
|
|
3134
|
+
fontSize: "0.85rem",
|
|
3135
|
+
color: "#6a737d",
|
|
3136
|
+
fontStyle: "italic",
|
|
3137
|
+
padding: "0.25rem 1rem",
|
|
3138
|
+
backgroundColor: "rgba(0, 0, 0, 0.06)"
|
|
3139
|
+
},
|
|
3140
|
+
// Last line of code block
|
|
3141
|
+
".cm-draftly-code-block-line-end": {
|
|
3142
|
+
borderBottomLeftRadius: "var(--radius)",
|
|
3143
|
+
borderBottomRightRadius: "var(--radius)",
|
|
3144
|
+
borderBottom: "1px solid var(--color-border)",
|
|
3145
|
+
paddingTop: "0.5rem !important",
|
|
3146
|
+
"& br": {
|
|
3147
|
+
display: "none"
|
|
3148
|
+
}
|
|
3149
|
+
},
|
|
3150
|
+
// Fence markers (```)
|
|
3151
|
+
".cm-draftly-code-fence": {
|
|
3152
|
+
color: "#6a737d",
|
|
3153
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)"
|
|
3154
|
+
},
|
|
3155
|
+
// Line numbers
|
|
3156
|
+
".cm-draftly-code-line-numbered": {
|
|
3157
|
+
paddingLeft: "calc(var(--line-num-width, 2ch) + 1rem) !important",
|
|
3158
|
+
position: "relative",
|
|
3159
|
+
"&::before": {
|
|
3160
|
+
content: "attr(data-line-num)",
|
|
3161
|
+
position: "absolute",
|
|
3162
|
+
left: "0.5rem",
|
|
3163
|
+
top: "0.2rem",
|
|
3164
|
+
width: "var(--line-num-width, 2ch)",
|
|
3165
|
+
textAlign: "right",
|
|
3166
|
+
color: "#6a737d",
|
|
3167
|
+
opacity: "0.6",
|
|
3168
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
3169
|
+
fontSize: "0.85rem",
|
|
3170
|
+
userSelect: "none"
|
|
3171
|
+
}
|
|
3172
|
+
},
|
|
3173
|
+
".cm-draftly-code-line-numbered-diff": {
|
|
3174
|
+
paddingLeft: "calc(var(--line-num-old-width, 2ch) + var(--line-num-new-width, 2ch) + 2.75rem) !important",
|
|
3175
|
+
position: "relative",
|
|
3176
|
+
"&::before": {
|
|
3177
|
+
content: "attr(data-line-num-old)",
|
|
3178
|
+
position: "absolute",
|
|
3179
|
+
left: "0.5rem",
|
|
3180
|
+
top: "0.2rem",
|
|
3181
|
+
width: "var(--line-num-old-width, 2ch)",
|
|
3182
|
+
textAlign: "right",
|
|
3183
|
+
color: "#6a737d",
|
|
3184
|
+
opacity: "0.6",
|
|
3185
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
3186
|
+
fontSize: "0.85rem",
|
|
3187
|
+
userSelect: "none"
|
|
3188
|
+
},
|
|
3189
|
+
"&::after": {
|
|
3190
|
+
content: 'attr(data-line-num-new) " " attr(data-diff-marker)',
|
|
3191
|
+
position: "absolute",
|
|
3192
|
+
left: "calc(0.5rem + var(--line-num-old-width, 2ch) + 0.75rem)",
|
|
3193
|
+
top: "0.2rem",
|
|
3194
|
+
width: "calc(var(--line-num-new-width, 2ch) + 2ch)",
|
|
3195
|
+
textAlign: "right",
|
|
3196
|
+
color: "#6a737d",
|
|
3197
|
+
opacity: "0.6",
|
|
3198
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
3199
|
+
fontSize: "0.85rem",
|
|
3200
|
+
userSelect: "none"
|
|
3201
|
+
},
|
|
3202
|
+
"&.cm-draftly-code-line-diff-gutter": {
|
|
3203
|
+
paddingLeft: "calc(var(--line-num-width, 2ch) + 2rem) !important",
|
|
3204
|
+
"&::after": {
|
|
3205
|
+
content: "attr(data-diff-marker)",
|
|
3206
|
+
position: "absolute",
|
|
3207
|
+
left: "calc(0.5rem + var(--line-num-width, 2ch) + 0.35rem)",
|
|
3208
|
+
top: "0.1rem",
|
|
3209
|
+
width: "1ch",
|
|
3210
|
+
textAlign: "right",
|
|
3211
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
3212
|
+
fontSize: "0.85rem",
|
|
3213
|
+
fontWeight: "700",
|
|
3214
|
+
userSelect: "none"
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
},
|
|
3218
|
+
// Preview: code lines (need block display for full-width highlights)
|
|
3219
|
+
".cm-draftly-code-line": {
|
|
3220
|
+
display: "block",
|
|
3221
|
+
position: "relative",
|
|
3222
|
+
paddingLeft: "1rem",
|
|
3223
|
+
paddingRight: "1rem",
|
|
3224
|
+
lineHeight: "1.5",
|
|
3225
|
+
borderLeft: "3px solid transparent"
|
|
3226
|
+
},
|
|
3227
|
+
// Line highlight
|
|
3228
|
+
".cm-draftly-code-line-highlight": {
|
|
3229
|
+
backgroundColor: "rgba(255, 220, 100, 0.2) !important",
|
|
3230
|
+
borderLeft: "3px solid #f0b429 !important"
|
|
3231
|
+
},
|
|
3232
|
+
".cm-draftly-code-line-diff-add": {
|
|
3233
|
+
color: "inherit",
|
|
3234
|
+
backgroundColor: "rgba(34, 197, 94, 0.12) !important",
|
|
3235
|
+
borderLeft: "3px solid #22c55e !important",
|
|
3236
|
+
"&.cm-draftly-code-line-diff-gutter::after": {
|
|
3237
|
+
color: "#16a34a"
|
|
3238
|
+
}
|
|
3239
|
+
},
|
|
3240
|
+
".cm-draftly-code-line-diff-del": {
|
|
3241
|
+
color: "inherit",
|
|
3242
|
+
backgroundColor: "rgba(239, 68, 68, 0.12) !important",
|
|
3243
|
+
borderLeft: "3px solid #ef4444 !important",
|
|
3244
|
+
"&.cm-draftly-code-line-diff-gutter::after": {
|
|
3245
|
+
color: "#dc2626"
|
|
3246
|
+
}
|
|
3247
|
+
},
|
|
3248
|
+
".cm-draftly-code-diff-sign-add": {
|
|
3249
|
+
color: "#16a34a",
|
|
3250
|
+
fontWeight: "700"
|
|
3251
|
+
},
|
|
3252
|
+
".cm-draftly-code-diff-sign-del": {
|
|
3253
|
+
color: "#dc2626",
|
|
3254
|
+
fontWeight: "700"
|
|
3255
|
+
},
|
|
3256
|
+
".cm-draftly-code-diff-mod-add": {
|
|
3257
|
+
color: "inherit",
|
|
3258
|
+
backgroundColor: "rgba(34, 197, 94, 0.25)",
|
|
3259
|
+
borderRadius: "2px",
|
|
3260
|
+
padding: "0.1rem 0"
|
|
3261
|
+
},
|
|
3262
|
+
".cm-draftly-code-diff-mod-del": {
|
|
3263
|
+
color: "inherit",
|
|
3264
|
+
backgroundColor: "rgba(239, 68, 68, 0.25)",
|
|
3265
|
+
borderRadius: "2px",
|
|
3266
|
+
padding: "0.1rem 0"
|
|
3267
|
+
},
|
|
3268
|
+
// Text highlight
|
|
3269
|
+
".cm-draftly-code-text-highlight": {
|
|
3270
|
+
color: "inherit",
|
|
3271
|
+
backgroundColor: "rgba(255, 220, 100, 0.4)",
|
|
3272
|
+
borderRadius: "2px",
|
|
3273
|
+
padding: "0.1rem 0"
|
|
3274
|
+
},
|
|
3275
|
+
// Preview: container wrapper
|
|
3276
|
+
".cm-draftly-code-container": {
|
|
3277
|
+
margin: "1rem 0",
|
|
3278
|
+
borderRadius: "var(--radius)",
|
|
3279
|
+
overflow: "hidden",
|
|
3280
|
+
border: "1px solid var(--color-border)",
|
|
3281
|
+
".cm-draftly-code-header": {
|
|
3282
|
+
borderRadius: "0",
|
|
3283
|
+
border: "none",
|
|
3284
|
+
borderBottom: "1px solid var(--color-border)"
|
|
3285
|
+
},
|
|
3286
|
+
".cm-draftly-code-block": {
|
|
3287
|
+
margin: "0",
|
|
3288
|
+
borderRadius: "0",
|
|
3289
|
+
border: "none",
|
|
3290
|
+
whiteSpace: "pre-wrap"
|
|
3291
|
+
},
|
|
3292
|
+
".cm-draftly-code-caption": {
|
|
3293
|
+
borderTop: "1px solid var(--color-border)"
|
|
3294
|
+
}
|
|
3295
|
+
},
|
|
3296
|
+
// Preview: standalone code block (not in container)
|
|
3297
|
+
".cm-draftly-code-block": {
|
|
3298
|
+
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
3299
|
+
fontSize: "0.9rem",
|
|
3300
|
+
backgroundColor: "rgba(0, 0, 0, 0.03)",
|
|
3301
|
+
padding: "1rem",
|
|
3302
|
+
overflow: "auto",
|
|
3303
|
+
position: "relative",
|
|
3304
|
+
borderRadius: "var(--radius)",
|
|
3305
|
+
border: "1px solid var(--color-border)",
|
|
3306
|
+
"&.cm-draftly-code-block-has-header": {
|
|
3307
|
+
borderTopLeftRadius: "0",
|
|
3308
|
+
borderTopRightRadius: "0",
|
|
3309
|
+
borderTop: "none",
|
|
3310
|
+
margin: "0",
|
|
3311
|
+
paddingTop: "0.5rem !important"
|
|
3312
|
+
},
|
|
3313
|
+
"&.cm-draftly-code-block-has-caption": {
|
|
3314
|
+
borderBottomLeftRadius: "0",
|
|
3315
|
+
borderBottomRightRadius: "0",
|
|
3316
|
+
borderBottom: "none",
|
|
3317
|
+
paddingBottom: "0.5rem !important"
|
|
3318
|
+
}
|
|
3319
|
+
}
|
|
3320
|
+
},
|
|
3321
|
+
dark: {
|
|
3322
|
+
".cm-draftly-code-inline": {
|
|
3323
|
+
backgroundColor: "rgba(255, 255, 255, 0.1)"
|
|
3324
|
+
},
|
|
3325
|
+
".cm-draftly-code-block-line": {
|
|
3326
|
+
backgroundColor: "rgba(255, 255, 255, 0.05)"
|
|
3327
|
+
},
|
|
3328
|
+
".cm-draftly-code-fence": {
|
|
3329
|
+
color: "#8b949e"
|
|
3330
|
+
},
|
|
3331
|
+
".cm-draftly-code-block": {
|
|
3332
|
+
backgroundColor: "rgba(255, 255, 255, 0.05)"
|
|
3333
|
+
},
|
|
3334
|
+
".cm-draftly-code-header": {
|
|
3335
|
+
backgroundColor: "rgba(255, 255, 255, 0.08)",
|
|
3336
|
+
".cm-draftly-code-header-lang": {
|
|
3337
|
+
color: "#8b949e"
|
|
3338
|
+
},
|
|
3339
|
+
".cm-draftly-code-copy-btn": {
|
|
3340
|
+
color: "#8b949e",
|
|
3341
|
+
"&:hover": {
|
|
3342
|
+
backgroundColor: "rgba(255, 255, 255, 0.1)"
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
},
|
|
3346
|
+
".cm-draftly-code-caption": {
|
|
3347
|
+
backgroundColor: "rgba(255, 255, 255, 0.05)"
|
|
3348
|
+
},
|
|
3349
|
+
".cm-draftly-code-line-numbered": {
|
|
3350
|
+
"&::before": {
|
|
3351
|
+
color: "#8b949e"
|
|
3352
|
+
}
|
|
3353
|
+
},
|
|
3354
|
+
".cm-draftly-code-line-numbered-diff": {
|
|
3355
|
+
"&::before": {
|
|
3356
|
+
color: "#8b949e"
|
|
3357
|
+
},
|
|
3358
|
+
"&::after": {
|
|
3359
|
+
color: "#8b949e"
|
|
3360
|
+
}
|
|
3361
|
+
},
|
|
3362
|
+
".cm-draftly-code-line-diff-gutter": {
|
|
3363
|
+
"&::after": {
|
|
3364
|
+
color: "#8b949e"
|
|
3365
|
+
}
|
|
3366
|
+
},
|
|
3367
|
+
".cm-draftly-code-line-highlight": {
|
|
3368
|
+
backgroundColor: "rgba(255, 220, 100, 0.15) !important",
|
|
3369
|
+
borderLeft: "3px solid #d9a520 !important"
|
|
3370
|
+
},
|
|
3371
|
+
".cm-draftly-code-line-diff-add": {
|
|
3372
|
+
backgroundColor: "rgba(34, 197, 94, 0.15) !important",
|
|
3373
|
+
borderLeft: "3px solid #22c55e !important",
|
|
3374
|
+
"&.cm-draftly-code-line-diff-gutter::after": {
|
|
3375
|
+
color: "#4ade80"
|
|
3376
|
+
}
|
|
3377
|
+
},
|
|
3378
|
+
".cm-draftly-code-line-diff-del": {
|
|
3379
|
+
backgroundColor: "rgba(239, 68, 68, 0.15) !important",
|
|
3380
|
+
borderLeft: "3px solid #ef4444 !important",
|
|
3381
|
+
"&.cm-draftly-code-line-diff-gutter::after": {
|
|
3382
|
+
color: "#f87171"
|
|
3383
|
+
}
|
|
3384
|
+
},
|
|
3385
|
+
".cm-draftly-code-diff-sign-add": {
|
|
3386
|
+
color: "#4ade80"
|
|
3387
|
+
},
|
|
3388
|
+
".cm-draftly-code-diff-sign-del": {
|
|
3389
|
+
color: "#f87171"
|
|
3390
|
+
},
|
|
3391
|
+
".cm-draftly-code-diff-mod-add": {
|
|
3392
|
+
backgroundColor: "rgba(34, 197, 94, 0.3)"
|
|
3393
|
+
},
|
|
3394
|
+
".cm-draftly-code-diff-mod-del": {
|
|
3395
|
+
backgroundColor: "rgba(239, 68, 68, 0.3)"
|
|
3396
|
+
},
|
|
3397
|
+
".cm-draftly-code-text-highlight": {
|
|
3398
|
+
backgroundColor: "rgba(255, 220, 100, 0.3)"
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
});
|
|
3402
|
+
|
|
3403
|
+
// src/plugins/code-plugin.ts
|
|
3404
|
+
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>`;
|
|
3405
|
+
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>`;
|
|
3406
|
+
var COPY_RESET_DELAY = 2e3;
|
|
3407
|
+
var CODE_FENCE = "```";
|
|
3408
|
+
var QUOTED_INFO_PATTERN = /(\w+)="([^"]*)"/g;
|
|
3409
|
+
var TEXT_HIGHLIGHT_PATTERN = /\/([^/]+)\/(?:(\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*))?/g;
|
|
3410
|
+
var codeMarkDecorations = {
|
|
3411
|
+
// Inline code
|
|
3412
|
+
"inline-code": Decoration.mark({ class: "cm-draftly-code-inline" }),
|
|
3413
|
+
"inline-mark": Decoration.replace({}),
|
|
3414
|
+
// Fenced code block
|
|
3415
|
+
"code-block-line": Decoration.line({ class: "cm-draftly-code-block-line" }),
|
|
3416
|
+
"code-block-line-start": Decoration.line({ class: "cm-draftly-code-block-line-start" }),
|
|
2382
3417
|
"code-block-line-end": Decoration.line({ class: "cm-draftly-code-block-line-end" }),
|
|
2383
3418
|
"code-fence": Decoration.mark({ class: "cm-draftly-code-fence" }),
|
|
2384
3419
|
"code-hidden": Decoration.replace({}),
|
|
2385
3420
|
// Highlights
|
|
2386
3421
|
"code-line-highlight": Decoration.line({ class: "cm-draftly-code-line-highlight" }),
|
|
2387
|
-
"code-text-highlight": Decoration.mark({ class: "cm-draftly-code-text-highlight" })
|
|
3422
|
+
"code-text-highlight": Decoration.mark({ class: "cm-draftly-code-text-highlight" }),
|
|
3423
|
+
// Diff preview
|
|
3424
|
+
"diff-line-add": Decoration.line({ class: "cm-draftly-code-line-diff-add" }),
|
|
3425
|
+
"diff-line-del": Decoration.line({ class: "cm-draftly-code-line-diff-del" }),
|
|
3426
|
+
"diff-sign-add": Decoration.mark({ class: "cm-draftly-code-diff-sign-add" }),
|
|
3427
|
+
"diff-sign-del": Decoration.mark({ class: "cm-draftly-code-diff-sign-del" }),
|
|
3428
|
+
"diff-mod-add": Decoration.mark({ class: "cm-draftly-code-diff-mod-add" }),
|
|
3429
|
+
"diff-mod-del": Decoration.mark({ class: "cm-draftly-code-diff-mod-del" }),
|
|
3430
|
+
"diff-escape-hidden": Decoration.replace({})
|
|
2388
3431
|
};
|
|
2389
3432
|
var CodeBlockHeaderWidget = class extends WidgetType {
|
|
2390
3433
|
constructor(props, codeContent) {
|
|
@@ -2470,11 +3513,12 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2470
3513
|
version = "1.0.0";
|
|
2471
3514
|
decorationPriority = 25;
|
|
2472
3515
|
requiredNodes = ["InlineCode", "FencedCode", "CodeMark", "CodeInfo", "CodeText"];
|
|
3516
|
+
parserCache = /* @__PURE__ */ new Map();
|
|
2473
3517
|
/**
|
|
2474
3518
|
* Plugin theme
|
|
2475
3519
|
*/
|
|
2476
3520
|
get theme() {
|
|
2477
|
-
return
|
|
3521
|
+
return codePluginTheme;
|
|
2478
3522
|
}
|
|
2479
3523
|
/**
|
|
2480
3524
|
* Keyboard shortcuts for code formatting
|
|
@@ -2493,6 +3537,15 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2493
3537
|
}
|
|
2494
3538
|
];
|
|
2495
3539
|
}
|
|
3540
|
+
/**
|
|
3541
|
+
* Intercepts backtick typing to wrap selected text as inline code.
|
|
3542
|
+
*
|
|
3543
|
+
* If user types '`' while text is selected, wraps each selected range
|
|
3544
|
+
* with backticks (selected -> `selected`).
|
|
3545
|
+
*/
|
|
3546
|
+
getExtensions() {
|
|
3547
|
+
return [createWrapSelectionInputHandler({ "`": "`" })];
|
|
3548
|
+
}
|
|
2496
3549
|
/**
|
|
2497
3550
|
* Toggle code block on current line or selected lines
|
|
2498
3551
|
*/
|
|
@@ -2505,7 +3558,7 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2505
3558
|
const nextLineNum = endLine.number < state.doc.lines ? endLine.number + 1 : endLine.number;
|
|
2506
3559
|
const prevLine = state.doc.line(prevLineNum);
|
|
2507
3560
|
const nextLine = state.doc.line(nextLineNum);
|
|
2508
|
-
const isWrapped = prevLine.text.trim().startsWith(
|
|
3561
|
+
const isWrapped = prevLine.text.trim().startsWith(CODE_FENCE) && nextLine.text.trim() === CODE_FENCE && prevLineNum !== startLine.number && nextLineNum !== endLine.number;
|
|
2509
3562
|
if (isWrapped) {
|
|
2510
3563
|
view.dispatch({
|
|
2511
3564
|
changes: [
|
|
@@ -2516,8 +3569,10 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2516
3569
|
]
|
|
2517
3570
|
});
|
|
2518
3571
|
} else {
|
|
2519
|
-
const openFence =
|
|
2520
|
-
|
|
3572
|
+
const openFence = `${CODE_FENCE}
|
|
3573
|
+
`;
|
|
3574
|
+
const closeFence = `
|
|
3575
|
+
${CODE_FENCE}`;
|
|
2521
3576
|
view.dispatch({
|
|
2522
3577
|
changes: [
|
|
2523
3578
|
{ from: startLine.from, insert: openFence },
|
|
@@ -2546,6 +3601,7 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2546
3601
|
* lineNumbers: 5,
|
|
2547
3602
|
* title: "hello.tsx",
|
|
2548
3603
|
* copy: true,
|
|
3604
|
+
* diff: false,
|
|
2549
3605
|
* highlightLines: [2,3,4,5],
|
|
2550
3606
|
* highlightText: [{ pattern: "Hello", instances: [3,4,5] }]
|
|
2551
3607
|
* }
|
|
@@ -2557,14 +3613,21 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2557
3613
|
return props;
|
|
2558
3614
|
}
|
|
2559
3615
|
let remaining = codeInfo.trim();
|
|
2560
|
-
const
|
|
2561
|
-
if (
|
|
2562
|
-
|
|
2563
|
-
|
|
3616
|
+
const firstTokenMatch = remaining.match(/^([^\s]+)/);
|
|
3617
|
+
if (firstTokenMatch && firstTokenMatch[1]) {
|
|
3618
|
+
const firstToken = firstTokenMatch[1];
|
|
3619
|
+
const normalizedToken = firstToken.toLowerCase();
|
|
3620
|
+
const isLineNumberDirective = /^(?:line-numbers|linenumbers|showlinenumbers)(?:\{\d+\})?$/.test(
|
|
3621
|
+
normalizedToken
|
|
3622
|
+
);
|
|
3623
|
+
const isKnownDirective = isLineNumberDirective || normalizedToken === "copy" || normalizedToken === "diff" || normalizedToken.startsWith("{") || normalizedToken.startsWith("/");
|
|
3624
|
+
if (!isKnownDirective) {
|
|
3625
|
+
props.language = firstToken;
|
|
3626
|
+
remaining = remaining.slice(firstToken.length).trim();
|
|
3627
|
+
}
|
|
2564
3628
|
}
|
|
2565
|
-
const quotedPattern = /(\w+)="([^"]*)"/g;
|
|
2566
3629
|
let quotedMatch;
|
|
2567
|
-
while ((quotedMatch =
|
|
3630
|
+
while ((quotedMatch = QUOTED_INFO_PATTERN.exec(remaining)) !== null) {
|
|
2568
3631
|
const key = quotedMatch[1]?.toLowerCase();
|
|
2569
3632
|
const value = quotedMatch[2];
|
|
2570
3633
|
if (key === "title" && value !== void 0) {
|
|
@@ -2573,13 +3636,13 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2573
3636
|
props.caption = value;
|
|
2574
3637
|
}
|
|
2575
3638
|
}
|
|
2576
|
-
remaining = remaining.replace(
|
|
2577
|
-
const lineNumbersMatch = remaining.match(
|
|
3639
|
+
remaining = remaining.replace(QUOTED_INFO_PATTERN, "").trim();
|
|
3640
|
+
const lineNumbersMatch = remaining.match(/\b(?:line-numbers|lineNumbers|showLineNumbers)(?:\{(\d+)\})?/i);
|
|
2578
3641
|
if (lineNumbersMatch) {
|
|
2579
3642
|
if (lineNumbersMatch[1]) {
|
|
2580
|
-
props.
|
|
3643
|
+
props.showLineNumbers = parseInt(lineNumbersMatch[1], 10);
|
|
2581
3644
|
} else {
|
|
2582
|
-
props.
|
|
3645
|
+
props.showLineNumbers = true;
|
|
2583
3646
|
}
|
|
2584
3647
|
remaining = remaining.replace(lineNumbersMatch[0], "").trim();
|
|
2585
3648
|
}
|
|
@@ -2587,52 +3650,27 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2587
3650
|
props.copy = true;
|
|
2588
3651
|
remaining = remaining.replace(/\bcopy\b/, "").trim();
|
|
2589
3652
|
}
|
|
3653
|
+
if (/\bdiff\b/.test(remaining)) {
|
|
3654
|
+
props.diff = true;
|
|
3655
|
+
remaining = remaining.replace(/\bdiff\b/, "").trim();
|
|
3656
|
+
}
|
|
2590
3657
|
const lineHighlightMatch = remaining.match(/\{([^}]+)\}/);
|
|
2591
3658
|
if (lineHighlightMatch && lineHighlightMatch[1]) {
|
|
2592
|
-
const highlightLines = [];
|
|
2593
|
-
const parts = lineHighlightMatch[1].split(",");
|
|
2594
|
-
for (const part of parts) {
|
|
2595
|
-
const trimmed = part.trim();
|
|
2596
|
-
const rangeMatch = trimmed.match(/^(\d+)-(\d+)$/);
|
|
2597
|
-
if (rangeMatch && rangeMatch[1] && rangeMatch[2]) {
|
|
2598
|
-
const start = parseInt(rangeMatch[1], 10);
|
|
2599
|
-
const end = parseInt(rangeMatch[2], 10);
|
|
2600
|
-
for (let i = start; i <= end; i++) {
|
|
2601
|
-
highlightLines.push(i);
|
|
2602
|
-
}
|
|
2603
|
-
} else if (/^\d+$/.test(trimmed)) {
|
|
2604
|
-
highlightLines.push(parseInt(trimmed, 10));
|
|
2605
|
-
}
|
|
2606
|
-
}
|
|
3659
|
+
const highlightLines = this.parseNumberList(lineHighlightMatch[1]);
|
|
2607
3660
|
if (highlightLines.length > 0) {
|
|
2608
3661
|
props.highlightLines = highlightLines;
|
|
2609
3662
|
}
|
|
2610
3663
|
remaining = remaining.replace(lineHighlightMatch[0], "").trim();
|
|
2611
3664
|
}
|
|
2612
|
-
const textHighlightPattern = /\/([^/]+)\/(?:(\d+(?:-\d+)?(?:,\d+(?:-\d+)?)*))?/g;
|
|
2613
3665
|
let textMatch;
|
|
2614
3666
|
const highlightText = [];
|
|
2615
|
-
while ((textMatch =
|
|
3667
|
+
while ((textMatch = TEXT_HIGHLIGHT_PATTERN.exec(remaining)) !== null) {
|
|
2616
3668
|
if (!textMatch[1]) continue;
|
|
2617
3669
|
const highlight = {
|
|
2618
3670
|
pattern: textMatch[1]
|
|
2619
3671
|
};
|
|
2620
3672
|
if (textMatch[2]) {
|
|
2621
|
-
const
|
|
2622
|
-
const instances = [];
|
|
2623
|
-
const instanceParts = instanceStr.split(",");
|
|
2624
|
-
for (const part of instanceParts) {
|
|
2625
|
-
const rangeMatch = part.match(/^(\d+)-(\d+)$/);
|
|
2626
|
-
if (rangeMatch && rangeMatch[1] && rangeMatch[2]) {
|
|
2627
|
-
const start = parseInt(rangeMatch[1], 10);
|
|
2628
|
-
const end = parseInt(rangeMatch[2], 10);
|
|
2629
|
-
for (let i = start; i <= end; i++) {
|
|
2630
|
-
instances.push(i);
|
|
2631
|
-
}
|
|
2632
|
-
} else if (/^\d+$/.test(part)) {
|
|
2633
|
-
instances.push(parseInt(part, 10));
|
|
2634
|
-
}
|
|
2635
|
-
}
|
|
3673
|
+
const instances = this.parseNumberList(textMatch[2]);
|
|
2636
3674
|
if (instances.length > 0) {
|
|
2637
3675
|
highlight.instances = instances;
|
|
2638
3676
|
}
|
|
@@ -2649,148 +3687,244 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2649
3687
|
* Handles line numbers, highlights, header/caption widgets, and fence visibility.
|
|
2650
3688
|
*/
|
|
2651
3689
|
buildDecorations(ctx) {
|
|
2652
|
-
const
|
|
2653
|
-
const tree = syntaxTree(view.state);
|
|
3690
|
+
const tree = syntaxTree(ctx.view.state);
|
|
2654
3691
|
tree.iterate({
|
|
2655
3692
|
enter: (node) => {
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
const cursorInRange = ctx.selectionOverlapsRange(from, to);
|
|
2660
|
-
if (!cursorInRange) {
|
|
2661
|
-
for (let child = node.node.firstChild; child; child = child.nextSibling) {
|
|
2662
|
-
if (child.name === "CodeMark") {
|
|
2663
|
-
decorations.push(codeMarkDecorations["inline-mark"].range(child.from, child.to));
|
|
2664
|
-
}
|
|
2665
|
-
}
|
|
2666
|
-
}
|
|
3693
|
+
if (node.name === "InlineCode") {
|
|
3694
|
+
this.decorateInlineCode(node, ctx);
|
|
3695
|
+
return;
|
|
2667
3696
|
}
|
|
2668
|
-
if (name === "FencedCode") {
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
3697
|
+
if (node.name === "FencedCode") {
|
|
3698
|
+
this.decorateFencedCode(node, ctx);
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
});
|
|
3702
|
+
}
|
|
3703
|
+
decorateInlineCode(node, ctx) {
|
|
3704
|
+
const { from, to } = node;
|
|
3705
|
+
ctx.decorations.push(codeMarkDecorations["inline-code"].range(from, to));
|
|
3706
|
+
if (ctx.selectionOverlapsRange(from, to)) {
|
|
3707
|
+
return;
|
|
3708
|
+
}
|
|
3709
|
+
for (let child = node.node.firstChild; child; child = child.nextSibling) {
|
|
3710
|
+
if (child.name === "CodeMark") {
|
|
3711
|
+
ctx.decorations.push(codeMarkDecorations["inline-mark"].range(child.from, child.to));
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
decorateFencedCode(node, ctx) {
|
|
3716
|
+
const { view, decorations } = ctx;
|
|
3717
|
+
const nodeLineStart = view.state.doc.lineAt(node.from);
|
|
3718
|
+
const nodeLineEnd = view.state.doc.lineAt(node.to);
|
|
3719
|
+
const cursorInRange = ctx.selectionOverlapsRange(nodeLineStart.from, nodeLineEnd.to);
|
|
3720
|
+
let infoProps = { language: "" };
|
|
3721
|
+
let codeContent = "";
|
|
3722
|
+
for (let child = node.node.firstChild; child; child = child.nextSibling) {
|
|
3723
|
+
if (child.name === "CodeInfo") {
|
|
3724
|
+
infoProps = this.parseCodeInfo(view.state.sliceDoc(child.from, child.to).trim());
|
|
3725
|
+
}
|
|
3726
|
+
if (child.name === "CodeText") {
|
|
3727
|
+
codeContent = view.state.sliceDoc(child.from, child.to);
|
|
3728
|
+
}
|
|
3729
|
+
}
|
|
3730
|
+
const codeLines = [];
|
|
3731
|
+
for (let i = nodeLineStart.number + 1; i <= nodeLineEnd.number - 1; i++) {
|
|
3732
|
+
const codeLine = view.state.doc.line(i);
|
|
3733
|
+
codeLines.push(view.state.sliceDoc(codeLine.from, codeLine.to));
|
|
3734
|
+
}
|
|
3735
|
+
const totalCodeLines = nodeLineEnd.number - nodeLineStart.number - 1;
|
|
3736
|
+
const startLineNum = typeof infoProps.showLineNumbers === "number" ? infoProps.showLineNumbers : 1;
|
|
3737
|
+
const maxLineNum = startLineNum + totalCodeLines - 1;
|
|
3738
|
+
const lineNumWidth = Math.max(String(maxLineNum).length, String(startLineNum).length);
|
|
3739
|
+
const highlightInstanceCounters = new Array(infoProps.highlightText?.length ?? 0).fill(0);
|
|
3740
|
+
const diffStates = infoProps.diff ? this.analyzeDiffLines(codeLines) : [];
|
|
3741
|
+
const diffDisplayLineNumbers = infoProps.diff ? this.computeDiffDisplayLineNumbers(diffStates, startLineNum) : [];
|
|
3742
|
+
const displayLineNumbers = infoProps.diff ? diffDisplayLineNumbers.map((numbers, index) => numbers.newLine ?? numbers.oldLine ?? startLineNum + index) : codeLines.map((_, index) => startLineNum + index);
|
|
3743
|
+
const diffHighlightLineNumbers = infoProps.diff ? this.computeDiffDisplayLineNumbers(diffStates, startLineNum).map(
|
|
3744
|
+
(numbers, index) => numbers.newLine ?? numbers.oldLine ?? startLineNum + index
|
|
3745
|
+
) : [];
|
|
3746
|
+
const maxOldDiffLineNum = diffDisplayLineNumbers.reduce((max, numbers) => {
|
|
3747
|
+
const oldLine = numbers.oldLine ?? 0;
|
|
3748
|
+
return oldLine > max ? oldLine : max;
|
|
3749
|
+
}, startLineNum);
|
|
3750
|
+
const maxNewDiffLineNum = diffDisplayLineNumbers.reduce((max, numbers) => {
|
|
3751
|
+
const newLine = numbers.newLine ?? 0;
|
|
3752
|
+
return newLine > max ? newLine : max;
|
|
3753
|
+
}, startLineNum);
|
|
3754
|
+
const diffOldLineNumWidth = Math.max(String(startLineNum).length, String(maxOldDiffLineNum).length);
|
|
3755
|
+
const diffNewLineNumWidth = Math.max(String(startLineNum).length, String(maxNewDiffLineNum).length);
|
|
3756
|
+
const shouldShowHeader = !cursorInRange && (infoProps.title || infoProps.copy || infoProps.language);
|
|
3757
|
+
const shouldShowCaption = !cursorInRange && !!infoProps.caption;
|
|
3758
|
+
if (shouldShowHeader) {
|
|
3759
|
+
decorations.push(
|
|
3760
|
+
Decoration.widget({
|
|
3761
|
+
widget: new CodeBlockHeaderWidget(infoProps, codeContent),
|
|
3762
|
+
block: false,
|
|
3763
|
+
side: -1
|
|
3764
|
+
}).range(nodeLineStart.from)
|
|
3765
|
+
);
|
|
3766
|
+
}
|
|
3767
|
+
let codeLineIndex = 0;
|
|
3768
|
+
for (let lineNumber = nodeLineStart.number; lineNumber <= nodeLineEnd.number; lineNumber++) {
|
|
3769
|
+
const line = view.state.doc.line(lineNumber);
|
|
3770
|
+
const isFenceLine = lineNumber === nodeLineStart.number || lineNumber === nodeLineEnd.number;
|
|
3771
|
+
const relativeLineNum = displayLineNumbers[codeLineIndex] ?? startLineNum + codeLineIndex;
|
|
3772
|
+
decorations.push(codeMarkDecorations["code-block-line"].range(line.from));
|
|
3773
|
+
if (lineNumber === nodeLineStart.number) {
|
|
3774
|
+
decorations.push(codeMarkDecorations["code-block-line-start"].range(line.from));
|
|
3775
|
+
if (shouldShowHeader) {
|
|
3776
|
+
decorations.push(Decoration.line({ class: "cm-draftly-code-block-has-header" }).range(line.from));
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3779
|
+
if (lineNumber === nodeLineEnd.number) {
|
|
3780
|
+
decorations.push(codeMarkDecorations["code-block-line-end"].range(line.from));
|
|
3781
|
+
if (shouldShowCaption) {
|
|
3782
|
+
decorations.push(Decoration.line({ class: "cm-draftly-code-block-has-caption" }).range(line.from));
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
if (!isFenceLine && infoProps.showLineNumbers && !infoProps.diff) {
|
|
3786
|
+
decorations.push(
|
|
3787
|
+
Decoration.line({
|
|
3788
|
+
class: "cm-draftly-code-line-numbered",
|
|
3789
|
+
attributes: {
|
|
3790
|
+
"data-line-num": String(relativeLineNum),
|
|
3791
|
+
style: `--line-num-width: ${lineNumWidth}ch`
|
|
2677
3792
|
}
|
|
2678
|
-
}
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
if (shouldShowHeader) {
|
|
2694
|
-
decorations.push(
|
|
2695
|
-
Decoration.widget({
|
|
2696
|
-
widget: new CodeBlockHeaderWidget(infoProps, codeContent),
|
|
2697
|
-
block: false
|
|
2698
|
-
}).range(nodeLineStart.from)
|
|
2699
|
-
);
|
|
2700
|
-
}
|
|
2701
|
-
for (let i = nodeLineStart.number; i <= nodeLineEnd.number; i++) {
|
|
2702
|
-
const line = view.state.doc.line(i);
|
|
2703
|
-
const isFenceLine = i === nodeLineStart.number || i === nodeLineEnd.number;
|
|
2704
|
-
const relativeLineNum = startLineNum + codeLineIndex;
|
|
2705
|
-
decorations.push(codeMarkDecorations["code-block-line"].range(line.from));
|
|
2706
|
-
if (i === nodeLineStart.number) {
|
|
2707
|
-
decorations.push(codeMarkDecorations["code-block-line-start"].range(line.from));
|
|
2708
|
-
if (shouldShowHeader) {
|
|
2709
|
-
decorations.push(
|
|
2710
|
-
Decoration.line({
|
|
2711
|
-
class: "cm-draftly-code-block-has-header"
|
|
2712
|
-
}).range(line.from)
|
|
2713
|
-
);
|
|
2714
|
-
}
|
|
2715
|
-
}
|
|
2716
|
-
if (i === nodeLineEnd.number) {
|
|
2717
|
-
decorations.push(codeMarkDecorations["code-block-line-end"].range(line.from));
|
|
2718
|
-
if (shouldShowCaption) {
|
|
2719
|
-
decorations.push(
|
|
2720
|
-
Decoration.line({
|
|
2721
|
-
class: "cm-draftly-code-block-has-caption"
|
|
2722
|
-
}).range(line.from)
|
|
2723
|
-
);
|
|
2724
|
-
}
|
|
2725
|
-
}
|
|
2726
|
-
if (!isFenceLine && infoProps.lineNumbers) {
|
|
2727
|
-
decorations.push(
|
|
2728
|
-
Decoration.line({
|
|
2729
|
-
class: "cm-draftly-code-line-numbered",
|
|
2730
|
-
attributes: {
|
|
2731
|
-
"data-line-num": String(relativeLineNum),
|
|
2732
|
-
style: `--line-num-width: ${lineNumWidth}ch`
|
|
2733
|
-
}
|
|
2734
|
-
}).range(line.from)
|
|
2735
|
-
);
|
|
2736
|
-
}
|
|
2737
|
-
if (!isFenceLine && infoProps.highlightLines) {
|
|
2738
|
-
if (infoProps.highlightLines.includes(codeLineIndex + 1)) {
|
|
2739
|
-
decorations.push(codeMarkDecorations["code-line-highlight"].range(line.from));
|
|
2740
|
-
}
|
|
2741
|
-
}
|
|
2742
|
-
if (!isFenceLine && infoProps.highlightText && infoProps.highlightText.length > 0) {
|
|
2743
|
-
const lineText = view.state.sliceDoc(line.from, line.to);
|
|
2744
|
-
for (const textHighlight of infoProps.highlightText) {
|
|
2745
|
-
try {
|
|
2746
|
-
const regex = new RegExp(textHighlight.pattern, "g");
|
|
2747
|
-
let match;
|
|
2748
|
-
let matchIndex = 0;
|
|
2749
|
-
while ((match = regex.exec(lineText)) !== null) {
|
|
2750
|
-
matchIndex++;
|
|
2751
|
-
const shouldHighlight = !textHighlight.instances || textHighlight.instances.includes(matchIndex);
|
|
2752
|
-
if (shouldHighlight) {
|
|
2753
|
-
const matchFrom = line.from + match.index;
|
|
2754
|
-
const matchTo = matchFrom + match[0].length;
|
|
2755
|
-
decorations.push(codeMarkDecorations["code-text-highlight"].range(matchFrom, matchTo));
|
|
2756
|
-
}
|
|
2757
|
-
}
|
|
2758
|
-
} catch {
|
|
2759
|
-
}
|
|
2760
|
-
}
|
|
2761
|
-
}
|
|
2762
|
-
if (!isFenceLine) {
|
|
2763
|
-
codeLineIndex++;
|
|
2764
|
-
}
|
|
2765
|
-
}
|
|
2766
|
-
for (let child = node.node.firstChild; child; child = child.nextSibling) {
|
|
2767
|
-
if (child.name === "CodeMark" || child.name === "CodeInfo") {
|
|
2768
|
-
if (cursorInRange) {
|
|
2769
|
-
decorations.push(codeMarkDecorations["code-fence"].range(child.from, child.to));
|
|
2770
|
-
} else {
|
|
2771
|
-
decorations.push(codeMarkDecorations["code-hidden"].range(child.from, child.to));
|
|
2772
|
-
}
|
|
3793
|
+
}).range(line.from)
|
|
3794
|
+
);
|
|
3795
|
+
}
|
|
3796
|
+
if (!isFenceLine && infoProps.showLineNumbers && infoProps.diff) {
|
|
3797
|
+
const diffLineNumbers = diffDisplayLineNumbers[codeLineIndex];
|
|
3798
|
+
const diffState = diffStates[codeLineIndex];
|
|
3799
|
+
const diffMarker = diffState?.kind === "addition" ? "+" : diffState?.kind === "deletion" ? "-" : " ";
|
|
3800
|
+
decorations.push(
|
|
3801
|
+
Decoration.line({
|
|
3802
|
+
class: "cm-draftly-code-line-numbered-diff",
|
|
3803
|
+
attributes: {
|
|
3804
|
+
"data-line-num-old": diffLineNumbers?.oldLine != null ? String(diffLineNumbers.oldLine) : "",
|
|
3805
|
+
"data-line-num-new": diffLineNumbers?.newLine != null ? String(diffLineNumbers.newLine) : "",
|
|
3806
|
+
"data-diff-marker": diffMarker,
|
|
3807
|
+
style: `--line-num-old-width: ${diffOldLineNumWidth}ch; --line-num-new-width: ${diffNewLineNumWidth}ch`
|
|
2773
3808
|
}
|
|
3809
|
+
}).range(line.from)
|
|
3810
|
+
);
|
|
3811
|
+
}
|
|
3812
|
+
if (!isFenceLine && infoProps.diff) {
|
|
3813
|
+
this.decorateDiffLine(
|
|
3814
|
+
line,
|
|
3815
|
+
codeLineIndex,
|
|
3816
|
+
diffStates,
|
|
3817
|
+
cursorInRange,
|
|
3818
|
+
!infoProps.showLineNumbers,
|
|
3819
|
+
decorations
|
|
3820
|
+
);
|
|
3821
|
+
}
|
|
3822
|
+
if (!isFenceLine && infoProps.highlightLines) {
|
|
3823
|
+
const highlightLineNumber = infoProps.diff ? diffHighlightLineNumbers[codeLineIndex] ?? codeLineIndex + 1 : startLineNum + codeLineIndex;
|
|
3824
|
+
if (infoProps.highlightLines.includes(highlightLineNumber)) {
|
|
3825
|
+
decorations.push(codeMarkDecorations["code-line-highlight"].range(line.from));
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
if (!isFenceLine && infoProps.highlightText?.length) {
|
|
3829
|
+
this.decorateTextHighlights(
|
|
3830
|
+
line.from,
|
|
3831
|
+
view.state.sliceDoc(line.from, line.to),
|
|
3832
|
+
infoProps.highlightText,
|
|
3833
|
+
highlightInstanceCounters,
|
|
3834
|
+
decorations
|
|
3835
|
+
);
|
|
3836
|
+
}
|
|
3837
|
+
if (!isFenceLine) {
|
|
3838
|
+
codeLineIndex++;
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
this.decorateFenceMarkers(node.node, cursorInRange, decorations);
|
|
3842
|
+
if (!cursorInRange && infoProps.caption) {
|
|
3843
|
+
decorations.push(
|
|
3844
|
+
Decoration.widget({
|
|
3845
|
+
widget: new CodeBlockCaptionWidget(infoProps.caption),
|
|
3846
|
+
block: false,
|
|
3847
|
+
side: 1
|
|
3848
|
+
}).range(nodeLineEnd.to)
|
|
3849
|
+
);
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
decorateFenceMarkers(node, cursorInRange, decorations) {
|
|
3853
|
+
for (let child = node.firstChild; child; child = child.nextSibling) {
|
|
3854
|
+
if (child.name === "CodeMark" || child.name === "CodeInfo") {
|
|
3855
|
+
decorations.push(
|
|
3856
|
+
(cursorInRange ? codeMarkDecorations["code-fence"] : codeMarkDecorations["code-hidden"]).range(
|
|
3857
|
+
child.from,
|
|
3858
|
+
child.to
|
|
3859
|
+
)
|
|
3860
|
+
);
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
}
|
|
3864
|
+
decorateDiffLine(line, codeLineIndex, diffStates, cursorInRange, showDiffMarkerGutter, decorations) {
|
|
3865
|
+
const diffState = diffStates[codeLineIndex];
|
|
3866
|
+
const diffMarker = diffState?.kind === "addition" ? "+" : diffState?.kind === "deletion" ? "-" : " ";
|
|
3867
|
+
if (showDiffMarkerGutter) {
|
|
3868
|
+
decorations.push(
|
|
3869
|
+
Decoration.line({
|
|
3870
|
+
class: "cm-draftly-code-line-diff-gutter",
|
|
3871
|
+
attributes: {
|
|
3872
|
+
"data-diff-marker": diffMarker
|
|
2774
3873
|
}
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
3874
|
+
}).range(line.from)
|
|
3875
|
+
);
|
|
3876
|
+
}
|
|
3877
|
+
if (diffState?.kind === "addition") {
|
|
3878
|
+
decorations.push(codeMarkDecorations["diff-line-add"].range(line.from));
|
|
3879
|
+
if (cursorInRange && line.to > line.from) {
|
|
3880
|
+
decorations.push(codeMarkDecorations["diff-sign-add"].range(line.from, line.from + 1));
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
if (diffState?.kind === "deletion") {
|
|
3884
|
+
decorations.push(codeMarkDecorations["diff-line-del"].range(line.from));
|
|
3885
|
+
if (cursorInRange && line.to > line.from) {
|
|
3886
|
+
decorations.push(codeMarkDecorations["diff-sign-del"].range(line.from, line.from + 1));
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3889
|
+
if (!cursorInRange && line.to > line.from && (diffState?.escapedMarker || diffState?.kind === "addition" || diffState?.kind === "deletion")) {
|
|
3890
|
+
decorations.push(codeMarkDecorations["diff-escape-hidden"].range(line.from, line.from + 1));
|
|
3891
|
+
}
|
|
3892
|
+
if (diffState?.modificationRanges?.length) {
|
|
3893
|
+
for (const [start, end] of diffState.modificationRanges) {
|
|
3894
|
+
const rangeFrom = line.from + diffState.contentOffset + start;
|
|
3895
|
+
const rangeTo = line.from + diffState.contentOffset + end;
|
|
3896
|
+
if (rangeTo > rangeFrom) {
|
|
3897
|
+
decorations.push(
|
|
3898
|
+
(diffState.kind === "addition" ? codeMarkDecorations["diff-mod-add"] : codeMarkDecorations["diff-mod-del"]).range(rangeFrom, rangeTo)
|
|
3899
|
+
);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
}
|
|
3904
|
+
decorateTextHighlights(lineFrom, lineText, highlights, instanceCounters, decorations) {
|
|
3905
|
+
for (const [highlightIndex, textHighlight] of highlights.entries()) {
|
|
3906
|
+
try {
|
|
3907
|
+
const regex = new RegExp(textHighlight.pattern, "g");
|
|
3908
|
+
let match;
|
|
3909
|
+
while ((match = regex.exec(lineText)) !== null) {
|
|
3910
|
+
instanceCounters[highlightIndex] = (instanceCounters[highlightIndex] ?? 0) + 1;
|
|
3911
|
+
const globalMatchIndex = instanceCounters[highlightIndex];
|
|
3912
|
+
const shouldHighlight = !textHighlight.instances || textHighlight.instances.includes(globalMatchIndex);
|
|
3913
|
+
if (shouldHighlight) {
|
|
3914
|
+
const matchFrom = lineFrom + match.index;
|
|
3915
|
+
const matchTo = matchFrom + match[0].length;
|
|
3916
|
+
decorations.push(codeMarkDecorations["code-text-highlight"].range(matchFrom, matchTo));
|
|
2784
3917
|
}
|
|
2785
3918
|
}
|
|
3919
|
+
} catch {
|
|
2786
3920
|
}
|
|
2787
|
-
}
|
|
3921
|
+
}
|
|
2788
3922
|
}
|
|
2789
3923
|
/**
|
|
2790
3924
|
* Render code elements to HTML for static preview.
|
|
2791
3925
|
* Applies syntax highlighting using @lezer/highlight.
|
|
2792
3926
|
*/
|
|
2793
|
-
renderToHTML(node,
|
|
3927
|
+
async renderToHTML(node, _children, ctx) {
|
|
2794
3928
|
if (node.name === "CodeMark") {
|
|
2795
3929
|
return "";
|
|
2796
3930
|
}
|
|
@@ -2800,7 +3934,7 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2800
3934
|
if (match && match[1]) {
|
|
2801
3935
|
content = match[1];
|
|
2802
3936
|
}
|
|
2803
|
-
return `<code class="cm-draftly-code-inline" style="padding: 0.1rem 0.25rem">${
|
|
3937
|
+
return `<code class="cm-draftly-code-inline" style="padding: 0.1rem 0.25rem">${this.escapeHtml(content)}</code>`;
|
|
2804
3938
|
}
|
|
2805
3939
|
if (node.name === "FencedCode") {
|
|
2806
3940
|
const content = ctx.sliceDoc(node.from, node.to);
|
|
@@ -2818,9 +3952,9 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2818
3952
|
html += `<div class="cm-draftly-code-header">`;
|
|
2819
3953
|
html += `<div class="cm-draftly-code-header-left">`;
|
|
2820
3954
|
if (props.title) {
|
|
2821
|
-
html += `<span class="cm-draftly-code-header-title">${
|
|
3955
|
+
html += `<span class="cm-draftly-code-header-title">${this.escapeHtml(props.title)}</span>`;
|
|
2822
3956
|
} else if (props.language) {
|
|
2823
|
-
html += `<span class="cm-draftly-code-header-lang">${
|
|
3957
|
+
html += `<span class="cm-draftly-code-header-lang">${this.escapeHtml(props.language)}</span>`;
|
|
2824
3958
|
}
|
|
2825
3959
|
html += `</div>`;
|
|
2826
3960
|
if (props.copy !== false) {
|
|
@@ -2833,32 +3967,83 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2833
3967
|
}
|
|
2834
3968
|
html += `</div>`;
|
|
2835
3969
|
}
|
|
2836
|
-
const startLineNum = typeof props.
|
|
2837
|
-
const
|
|
3970
|
+
const startLineNum = typeof props.showLineNumbers === "number" ? props.showLineNumbers : 1;
|
|
3971
|
+
const previewHighlightCounters = new Array(props.highlightText?.length ?? 0).fill(0);
|
|
3972
|
+
const diffStates = props.diff ? this.analyzeDiffLines(codeLines) : [];
|
|
3973
|
+
const previewDiffLineNumbers = props.diff ? this.computeDiffDisplayLineNumbers(diffStates, startLineNum) : [];
|
|
3974
|
+
const previewLineNumbers = props.diff ? previewDiffLineNumbers.map((numbers, index) => numbers.newLine ?? numbers.oldLine ?? startLineNum + index) : codeLines.map((_, index) => startLineNum + index);
|
|
3975
|
+
const previewHighlightLineNumbers = props.diff ? this.computeDiffDisplayLineNumbers(diffStates, startLineNum).map(
|
|
3976
|
+
(numbers, index) => numbers.newLine ?? numbers.oldLine ?? startLineNum + index
|
|
3977
|
+
) : [];
|
|
3978
|
+
const lineNumWidth = String(Math.max(...previewLineNumbers, startLineNum)).length;
|
|
3979
|
+
const previewOldLineNumWidth = String(
|
|
3980
|
+
Math.max(
|
|
3981
|
+
...previewDiffLineNumbers.map((numbers) => numbers.oldLine ?? 0),
|
|
3982
|
+
startLineNum
|
|
3983
|
+
)
|
|
3984
|
+
).length;
|
|
3985
|
+
const previewNewLineNumWidth = String(
|
|
3986
|
+
Math.max(
|
|
3987
|
+
...previewDiffLineNumbers.map((numbers) => numbers.newLine ?? 0),
|
|
3988
|
+
startLineNum
|
|
3989
|
+
)
|
|
3990
|
+
).length;
|
|
3991
|
+
const previewContentLines = props.diff ? diffStates.map((state) => state.content) : codeLines;
|
|
3992
|
+
const highlightedLines = await this.highlightCodeLines(
|
|
3993
|
+
previewContentLines.join("\n"),
|
|
3994
|
+
props.language || "",
|
|
3995
|
+
ctx.syntaxHighlighters
|
|
3996
|
+
);
|
|
2838
3997
|
const hasHeader = showHeader ? " cm-draftly-code-block-has-header" : "";
|
|
2839
3998
|
const hasCaption = props.caption ? " cm-draftly-code-block-has-caption" : "";
|
|
2840
|
-
html += `<pre class="cm-draftly-code-block${hasHeader}${hasCaption}"${props.language ? ` data-lang="${
|
|
3999
|
+
html += `<pre class="cm-draftly-code-block${hasHeader}${hasCaption}"${props.language ? ` data-lang="${this.escapeAttribute(props.language)}"` : ""}>`;
|
|
2841
4000
|
html += `<code>`;
|
|
2842
4001
|
codeLines.forEach((line, index) => {
|
|
2843
|
-
const lineNum = startLineNum + index;
|
|
2844
|
-
const
|
|
4002
|
+
const lineNum = previewLineNumbers[index] ?? startLineNum + index;
|
|
4003
|
+
const highlightLineNumber = props.diff ? previewHighlightLineNumbers[index] ?? startLineNum + index : startLineNum + index;
|
|
4004
|
+
const isHighlighted = props.highlightLines?.includes(highlightLineNumber);
|
|
4005
|
+
const diffState = props.diff ? diffStates[index] : void 0;
|
|
4006
|
+
const diffLineNumbers = props.diff ? previewDiffLineNumbers[index] : void 0;
|
|
2845
4007
|
const lineClasses = ["cm-draftly-code-line"];
|
|
2846
4008
|
if (isHighlighted) lineClasses.push("cm-draftly-code-line-highlight");
|
|
2847
|
-
if (props.
|
|
4009
|
+
if (props.showLineNumbers) {
|
|
4010
|
+
lineClasses.push(props.diff ? "cm-draftly-code-line-numbered-diff" : "cm-draftly-code-line-numbered");
|
|
4011
|
+
}
|
|
4012
|
+
if (diffState?.kind === "addition") lineClasses.push("cm-draftly-code-line-diff-add");
|
|
4013
|
+
if (diffState?.kind === "deletion") lineClasses.push("cm-draftly-code-line-diff-del");
|
|
2848
4014
|
const lineAttrs = [`class="${lineClasses.join(" ")}"`];
|
|
2849
|
-
if (props.
|
|
4015
|
+
if (props.showLineNumbers && !props.diff) {
|
|
2850
4016
|
lineAttrs.push(`data-line-num="${lineNum}"`);
|
|
2851
4017
|
lineAttrs.push(`style="--line-num-width: ${lineNumWidth}ch"`);
|
|
2852
4018
|
}
|
|
2853
|
-
|
|
4019
|
+
if (props.diff) {
|
|
4020
|
+
const diffMarker = diffState?.kind === "addition" ? "+" : diffState?.kind === "deletion" ? "-" : " ";
|
|
4021
|
+
if (props.showLineNumbers) {
|
|
4022
|
+
lineAttrs.push(`data-line-num-old="${diffLineNumbers?.oldLine != null ? diffLineNumbers.oldLine : ""}"`);
|
|
4023
|
+
lineAttrs.push(`data-line-num-new="${diffLineNumbers?.newLine != null ? diffLineNumbers.newLine : ""}"`);
|
|
4024
|
+
lineAttrs.push(`data-diff-marker="${diffMarker}"`);
|
|
4025
|
+
lineAttrs.push(
|
|
4026
|
+
`style="--line-num-old-width: ${previewOldLineNumWidth}ch; --line-num-new-width: ${previewNewLineNumWidth}ch"`
|
|
4027
|
+
);
|
|
4028
|
+
} else {
|
|
4029
|
+
lineAttrs.push(`data-diff-marker="${diffMarker}"`);
|
|
4030
|
+
lineClasses.push("cm-draftly-code-line-diff-gutter");
|
|
4031
|
+
lineAttrs[0] = `class="${lineClasses.join(" ")}"`;
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
const highlightedLine = highlightedLines[index] ?? this.escapeHtml(previewContentLines[index] ?? line);
|
|
4035
|
+
let lineContent = highlightedLine;
|
|
4036
|
+
if (diffState) {
|
|
4037
|
+
lineContent = this.renderDiffPreviewLine(diffState, highlightedLine);
|
|
4038
|
+
}
|
|
2854
4039
|
if (props.highlightText && props.highlightText.length > 0) {
|
|
2855
|
-
lineContent = this.applyTextHighlights(lineContent, props.highlightText);
|
|
4040
|
+
lineContent = this.applyTextHighlights(lineContent, props.highlightText, previewHighlightCounters);
|
|
2856
4041
|
}
|
|
2857
4042
|
html += `<span ${lineAttrs.join(" ")}>${lineContent || " "}</span>`;
|
|
2858
4043
|
});
|
|
2859
4044
|
html += `</code></pre>`;
|
|
2860
4045
|
if (props.caption) {
|
|
2861
|
-
html += `<div class="cm-draftly-code-caption">${
|
|
4046
|
+
html += `<div class="cm-draftly-code-caption">${this.escapeHtml(props.caption)}</div>`;
|
|
2862
4047
|
}
|
|
2863
4048
|
html += `</div>`;
|
|
2864
4049
|
return html;
|
|
@@ -2868,54 +4053,290 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2868
4053
|
}
|
|
2869
4054
|
return null;
|
|
2870
4055
|
}
|
|
4056
|
+
/** Parse comma-separated numbers and ranges (e.g. "1,3-5") into [1,3,4,5]. */
|
|
4057
|
+
parseNumberList(value) {
|
|
4058
|
+
const result = [];
|
|
4059
|
+
for (const part of value.split(",")) {
|
|
4060
|
+
const trimmed = part.trim();
|
|
4061
|
+
const rangeMatch = trimmed.match(/^(\d+)-(\d+)$/);
|
|
4062
|
+
if (rangeMatch && rangeMatch[1] && rangeMatch[2]) {
|
|
4063
|
+
const start = parseInt(rangeMatch[1], 10);
|
|
4064
|
+
const end = parseInt(rangeMatch[2], 10);
|
|
4065
|
+
for (let i = start; i <= end; i++) {
|
|
4066
|
+
result.push(i);
|
|
4067
|
+
}
|
|
4068
|
+
continue;
|
|
4069
|
+
}
|
|
4070
|
+
if (/^\d+$/.test(trimmed)) {
|
|
4071
|
+
result.push(parseInt(trimmed, 10));
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
return result;
|
|
4075
|
+
}
|
|
2871
4076
|
/**
|
|
2872
4077
|
* Highlight a single line of code using the language's Lezer parser.
|
|
2873
4078
|
* Falls back to sanitized plain text if the language is not supported.
|
|
2874
4079
|
*/
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
4080
|
+
async highlightCodeLines(code, lang, syntaxHighlighters) {
|
|
4081
|
+
const rawLines = code.split("\n");
|
|
4082
|
+
if (!lang || !code) {
|
|
4083
|
+
return rawLines.map((line) => this.escapeHtml(line));
|
|
2878
4084
|
}
|
|
2879
|
-
const
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
if (!langDesc || !langDesc.support) {
|
|
2883
|
-
return ctx.sanitize(line);
|
|
4085
|
+
const parser = await this.resolveLanguageParser(lang);
|
|
4086
|
+
if (!parser) {
|
|
4087
|
+
return rawLines.map((line) => this.escapeHtml(line));
|
|
2884
4088
|
}
|
|
2885
4089
|
try {
|
|
2886
|
-
const
|
|
2887
|
-
const
|
|
2888
|
-
let result = "";
|
|
4090
|
+
const tree = parser.parse(code);
|
|
4091
|
+
const highlightedLines = [""];
|
|
2889
4092
|
highlightCode(
|
|
2890
|
-
|
|
4093
|
+
code,
|
|
2891
4094
|
tree,
|
|
2892
|
-
|
|
4095
|
+
syntaxHighlighters && syntaxHighlighters.length > 0 ? syntaxHighlighters : [],
|
|
2893
4096
|
(text, classes2) => {
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
} else {
|
|
2897
|
-
result += ctx.sanitize(text);
|
|
2898
|
-
}
|
|
4097
|
+
const chunk = classes2 ? `<span class="${this.escapeAttribute(classes2)}">${this.escapeHtml(text)}</span>` : this.escapeHtml(text);
|
|
4098
|
+
highlightedLines[highlightedLines.length - 1] += chunk;
|
|
2899
4099
|
},
|
|
2900
4100
|
() => {
|
|
4101
|
+
highlightedLines.push("");
|
|
2901
4102
|
}
|
|
2902
|
-
// No newlines for single line
|
|
2903
4103
|
);
|
|
2904
|
-
return
|
|
4104
|
+
return rawLines.map((line, index) => highlightedLines[index] || this.escapeHtml(line));
|
|
2905
4105
|
} catch {
|
|
2906
|
-
return
|
|
4106
|
+
return rawLines.map((line) => this.escapeHtml(line));
|
|
2907
4107
|
}
|
|
2908
4108
|
}
|
|
4109
|
+
async resolveLanguageParser(lang) {
|
|
4110
|
+
const normalizedLang = this.normalizeLanguage(lang);
|
|
4111
|
+
if (!normalizedLang) return null;
|
|
4112
|
+
const cached = this.parserCache.get(normalizedLang);
|
|
4113
|
+
if (cached) return cached;
|
|
4114
|
+
const parserPromise = (async () => {
|
|
4115
|
+
const langDesc = LanguageDescription.matchLanguageName(languages, normalizedLang, true);
|
|
4116
|
+
if (!langDesc) return null;
|
|
4117
|
+
if (langDesc.support) {
|
|
4118
|
+
return langDesc.support.language.parser;
|
|
4119
|
+
}
|
|
4120
|
+
if (typeof langDesc.load === "function") {
|
|
4121
|
+
try {
|
|
4122
|
+
const support = await langDesc.load();
|
|
4123
|
+
return support.language.parser;
|
|
4124
|
+
} catch {
|
|
4125
|
+
return null;
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
return null;
|
|
4129
|
+
})();
|
|
4130
|
+
this.parserCache.set(normalizedLang, parserPromise);
|
|
4131
|
+
return parserPromise;
|
|
4132
|
+
}
|
|
4133
|
+
normalizeLanguage(lang) {
|
|
4134
|
+
const normalized = lang.trim().toLowerCase();
|
|
4135
|
+
if (!normalized) return "";
|
|
4136
|
+
const normalizedMap = {
|
|
4137
|
+
"c++": "cpp",
|
|
4138
|
+
"c#": "csharp",
|
|
4139
|
+
"f#": "fsharp",
|
|
4140
|
+
py: "python",
|
|
4141
|
+
js: "javascript",
|
|
4142
|
+
ts: "typescript",
|
|
4143
|
+
sh: "shell"
|
|
4144
|
+
};
|
|
4145
|
+
return normalizedMap[normalized] ?? normalized;
|
|
4146
|
+
}
|
|
4147
|
+
escapeHtml(value) {
|
|
4148
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4149
|
+
}
|
|
4150
|
+
escapeAttribute(value) {
|
|
4151
|
+
return this.escapeHtml(value).replace(/`/g, "`");
|
|
4152
|
+
}
|
|
4153
|
+
analyzeDiffLines(lines) {
|
|
4154
|
+
const states = lines.map((line) => this.parseDiffLineState(line));
|
|
4155
|
+
let index = 0;
|
|
4156
|
+
while (index < states.length) {
|
|
4157
|
+
if (states[index]?.kind !== "deletion") {
|
|
4158
|
+
index++;
|
|
4159
|
+
continue;
|
|
4160
|
+
}
|
|
4161
|
+
const deletionStart = index;
|
|
4162
|
+
while (index < states.length && states[index]?.kind === "deletion") {
|
|
4163
|
+
index++;
|
|
4164
|
+
}
|
|
4165
|
+
const deletionEnd = index;
|
|
4166
|
+
const additionStart = index;
|
|
4167
|
+
while (index < states.length && states[index]?.kind === "addition") {
|
|
4168
|
+
index++;
|
|
4169
|
+
}
|
|
4170
|
+
const additionEnd = index;
|
|
4171
|
+
if (additionStart === additionEnd) {
|
|
4172
|
+
continue;
|
|
4173
|
+
}
|
|
4174
|
+
const pairCount = Math.min(deletionEnd - deletionStart, additionEnd - additionStart);
|
|
4175
|
+
for (let pairIndex = 0; pairIndex < pairCount; pairIndex++) {
|
|
4176
|
+
const deletionState = states[deletionStart + pairIndex];
|
|
4177
|
+
const additionState = states[additionStart + pairIndex];
|
|
4178
|
+
if (!deletionState || !additionState) {
|
|
4179
|
+
continue;
|
|
4180
|
+
}
|
|
4181
|
+
const ranges = this.computeChangedRanges(deletionState.content, additionState.content);
|
|
4182
|
+
if (ranges.oldRanges.length > 0) {
|
|
4183
|
+
deletionState.modificationRanges = ranges.oldRanges;
|
|
4184
|
+
}
|
|
4185
|
+
if (ranges.newRanges.length > 0) {
|
|
4186
|
+
additionState.modificationRanges = ranges.newRanges;
|
|
4187
|
+
}
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
return states;
|
|
4191
|
+
}
|
|
4192
|
+
computeDiffDisplayLineNumbers(states, startLineNum) {
|
|
4193
|
+
const numbers = [];
|
|
4194
|
+
let oldLineNumber = startLineNum;
|
|
4195
|
+
let newLineNumber = startLineNum;
|
|
4196
|
+
for (const state of states) {
|
|
4197
|
+
if (state.kind === "deletion") {
|
|
4198
|
+
numbers.push({ oldLine: oldLineNumber, newLine: null });
|
|
4199
|
+
oldLineNumber++;
|
|
4200
|
+
continue;
|
|
4201
|
+
}
|
|
4202
|
+
if (state.kind === "addition") {
|
|
4203
|
+
numbers.push({ oldLine: null, newLine: newLineNumber });
|
|
4204
|
+
newLineNumber++;
|
|
4205
|
+
continue;
|
|
4206
|
+
}
|
|
4207
|
+
numbers.push({ oldLine: oldLineNumber, newLine: newLineNumber });
|
|
4208
|
+
oldLineNumber++;
|
|
4209
|
+
newLineNumber++;
|
|
4210
|
+
}
|
|
4211
|
+
return numbers;
|
|
4212
|
+
}
|
|
4213
|
+
parseDiffLineState(line) {
|
|
4214
|
+
const escapedMarker = line.startsWith("\\+") || line.startsWith("\\-");
|
|
4215
|
+
if (escapedMarker) {
|
|
4216
|
+
return {
|
|
4217
|
+
kind: "normal",
|
|
4218
|
+
content: line.slice(1),
|
|
4219
|
+
contentOffset: 1,
|
|
4220
|
+
escapedMarker: true
|
|
4221
|
+
};
|
|
4222
|
+
}
|
|
4223
|
+
if (line.startsWith("+")) {
|
|
4224
|
+
return {
|
|
4225
|
+
kind: "addition",
|
|
4226
|
+
content: line.slice(1),
|
|
4227
|
+
contentOffset: 1,
|
|
4228
|
+
escapedMarker: false
|
|
4229
|
+
};
|
|
4230
|
+
}
|
|
4231
|
+
if (line.startsWith("-")) {
|
|
4232
|
+
return {
|
|
4233
|
+
kind: "deletion",
|
|
4234
|
+
content: line.slice(1),
|
|
4235
|
+
contentOffset: 1,
|
|
4236
|
+
escapedMarker: false
|
|
4237
|
+
};
|
|
4238
|
+
}
|
|
4239
|
+
return {
|
|
4240
|
+
kind: "normal",
|
|
4241
|
+
content: line,
|
|
4242
|
+
contentOffset: 0,
|
|
4243
|
+
escapedMarker: false
|
|
4244
|
+
};
|
|
4245
|
+
}
|
|
4246
|
+
computeChangedRanges(oldText, newText) {
|
|
4247
|
+
let prefix = 0;
|
|
4248
|
+
while (prefix < oldText.length && prefix < newText.length && oldText[prefix] === newText[prefix]) {
|
|
4249
|
+
prefix++;
|
|
4250
|
+
}
|
|
4251
|
+
let oldSuffix = oldText.length;
|
|
4252
|
+
let newSuffix = newText.length;
|
|
4253
|
+
while (oldSuffix > prefix && newSuffix > prefix && oldText[oldSuffix - 1] === newText[newSuffix - 1]) {
|
|
4254
|
+
oldSuffix--;
|
|
4255
|
+
newSuffix--;
|
|
4256
|
+
}
|
|
4257
|
+
const oldRanges = [];
|
|
4258
|
+
const newRanges = [];
|
|
4259
|
+
if (oldSuffix > prefix) {
|
|
4260
|
+
oldRanges.push([prefix, oldSuffix]);
|
|
4261
|
+
}
|
|
4262
|
+
if (newSuffix > prefix) {
|
|
4263
|
+
newRanges.push([prefix, newSuffix]);
|
|
4264
|
+
}
|
|
4265
|
+
return { oldRanges, newRanges };
|
|
4266
|
+
}
|
|
4267
|
+
renderDiffPreviewLine(diffState, highlightedContent) {
|
|
4268
|
+
const modClass = diffState.kind === "addition" ? "cm-draftly-code-diff-mod-add" : diffState.kind === "deletion" ? "cm-draftly-code-diff-mod-del" : "";
|
|
4269
|
+
const baseHighlightedContent = highlightedContent || this.escapeHtml(diffState.content);
|
|
4270
|
+
const contentHtml = diffState.modificationRanges && modClass ? this.applyRangesToHighlightedHTML(baseHighlightedContent, diffState.modificationRanges, modClass) : baseHighlightedContent;
|
|
4271
|
+
return contentHtml || " ";
|
|
4272
|
+
}
|
|
4273
|
+
applyRangesToHighlightedHTML(htmlContent, ranges, className) {
|
|
4274
|
+
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]);
|
|
4275
|
+
if (normalizedRanges.length === 0 || !htmlContent) {
|
|
4276
|
+
return htmlContent;
|
|
4277
|
+
}
|
|
4278
|
+
const isInsideRange = (position) => {
|
|
4279
|
+
for (const [start, end] of normalizedRanges) {
|
|
4280
|
+
if (position >= start && position < end) return true;
|
|
4281
|
+
if (position < start) return false;
|
|
4282
|
+
}
|
|
4283
|
+
return false;
|
|
4284
|
+
};
|
|
4285
|
+
let result = "";
|
|
4286
|
+
let htmlIndex = 0;
|
|
4287
|
+
let textPosition = 0;
|
|
4288
|
+
let markOpen = false;
|
|
4289
|
+
while (htmlIndex < htmlContent.length) {
|
|
4290
|
+
const char = htmlContent[htmlIndex];
|
|
4291
|
+
if (char === "<") {
|
|
4292
|
+
const tagEnd = htmlContent.indexOf(">", htmlIndex);
|
|
4293
|
+
if (tagEnd === -1) {
|
|
4294
|
+
result += htmlContent.slice(htmlIndex);
|
|
4295
|
+
break;
|
|
4296
|
+
}
|
|
4297
|
+
result += htmlContent.slice(htmlIndex, tagEnd + 1);
|
|
4298
|
+
htmlIndex = tagEnd + 1;
|
|
4299
|
+
continue;
|
|
4300
|
+
}
|
|
4301
|
+
let token = char;
|
|
4302
|
+
if (char === "&") {
|
|
4303
|
+
const entityEnd = htmlContent.indexOf(";", htmlIndex);
|
|
4304
|
+
if (entityEnd !== -1) {
|
|
4305
|
+
token = htmlContent.slice(htmlIndex, entityEnd + 1);
|
|
4306
|
+
htmlIndex = entityEnd + 1;
|
|
4307
|
+
} else {
|
|
4308
|
+
htmlIndex += 1;
|
|
4309
|
+
}
|
|
4310
|
+
} else {
|
|
4311
|
+
htmlIndex += 1;
|
|
4312
|
+
}
|
|
4313
|
+
const shouldMark = isInsideRange(textPosition);
|
|
4314
|
+
if (shouldMark && !markOpen) {
|
|
4315
|
+
result += `<mark class="${className}">`;
|
|
4316
|
+
markOpen = true;
|
|
4317
|
+
}
|
|
4318
|
+
if (!shouldMark && markOpen) {
|
|
4319
|
+
result += "</mark>";
|
|
4320
|
+
markOpen = false;
|
|
4321
|
+
}
|
|
4322
|
+
result += token;
|
|
4323
|
+
textPosition += 1;
|
|
4324
|
+
}
|
|
4325
|
+
if (markOpen) {
|
|
4326
|
+
result += "</mark>";
|
|
4327
|
+
}
|
|
4328
|
+
return result;
|
|
4329
|
+
}
|
|
2909
4330
|
/**
|
|
2910
4331
|
* Apply text highlights (regex patterns) to already syntax-highlighted HTML.
|
|
2911
4332
|
* Wraps matched patterns in `<mark>` elements.
|
|
2912
4333
|
*/
|
|
2913
|
-
applyTextHighlights(htmlContent, highlights) {
|
|
4334
|
+
applyTextHighlights(htmlContent, highlights, instanceCounters) {
|
|
2914
4335
|
let result = htmlContent;
|
|
2915
|
-
for (const highlight of highlights) {
|
|
4336
|
+
for (const [highlightIndex, highlight] of highlights.entries()) {
|
|
2916
4337
|
try {
|
|
2917
4338
|
const regex = new RegExp(`(${highlight.pattern})`, "g");
|
|
2918
|
-
let matchCount = 0;
|
|
4339
|
+
let matchCount = instanceCounters?.[highlightIndex] ?? 0;
|
|
2919
4340
|
result = result.replace(regex, (match) => {
|
|
2920
4341
|
matchCount++;
|
|
2921
4342
|
const shouldHighlight = !highlight.instances || highlight.instances.includes(matchCount);
|
|
@@ -2924,252 +4345,15 @@ var CodePlugin = class extends DecorationPlugin {
|
|
|
2924
4345
|
}
|
|
2925
4346
|
return match;
|
|
2926
4347
|
});
|
|
4348
|
+
if (instanceCounters) {
|
|
4349
|
+
instanceCounters[highlightIndex] = matchCount;
|
|
4350
|
+
}
|
|
2927
4351
|
} catch {
|
|
2928
4352
|
}
|
|
2929
4353
|
}
|
|
2930
4354
|
return result;
|
|
2931
4355
|
}
|
|
2932
4356
|
};
|
|
2933
|
-
var theme10 = createTheme({
|
|
2934
|
-
default: {
|
|
2935
|
-
// Inline code
|
|
2936
|
-
".cm-draftly-code-inline": {
|
|
2937
|
-
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
2938
|
-
fontSize: "0.9rem",
|
|
2939
|
-
backgroundColor: "rgba(0, 0, 0, 0.05)",
|
|
2940
|
-
padding: "0.1rem 0.25rem",
|
|
2941
|
-
border: "1px solid var(--color-border)",
|
|
2942
|
-
borderRadius: "3px"
|
|
2943
|
-
},
|
|
2944
|
-
// Fenced code block lines
|
|
2945
|
-
".cm-draftly-code-block-line": {
|
|
2946
|
-
"--radius": "0.375rem",
|
|
2947
|
-
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
2948
|
-
fontSize: "0.9rem",
|
|
2949
|
-
backgroundColor: "rgba(0, 0, 0, 0.03)",
|
|
2950
|
-
padding: "0 1rem !important",
|
|
2951
|
-
lineHeight: "1.5",
|
|
2952
|
-
borderLeft: "1px solid var(--color-border)",
|
|
2953
|
-
borderRight: "1px solid var(--color-border)"
|
|
2954
|
-
},
|
|
2955
|
-
// First line of code block
|
|
2956
|
-
".cm-draftly-code-block-line-start": {
|
|
2957
|
-
borderTopLeftRadius: "var(--radius)",
|
|
2958
|
-
borderTopRightRadius: "var(--radius)",
|
|
2959
|
-
position: "relative",
|
|
2960
|
-
overflow: "hidden",
|
|
2961
|
-
borderTop: "1px solid var(--color-border)",
|
|
2962
|
-
paddingBottom: "0.5rem !important"
|
|
2963
|
-
},
|
|
2964
|
-
// Remove top radius when header is present
|
|
2965
|
-
".cm-draftly-code-block-has-header": {
|
|
2966
|
-
padding: "0 !important",
|
|
2967
|
-
paddingBottom: "0.5rem !important"
|
|
2968
|
-
},
|
|
2969
|
-
// Code block header widget
|
|
2970
|
-
".cm-draftly-code-header": {
|
|
2971
|
-
display: "flex",
|
|
2972
|
-
justifyContent: "space-between",
|
|
2973
|
-
alignItems: "center",
|
|
2974
|
-
padding: "0.25rem 1rem",
|
|
2975
|
-
backgroundColor: "rgba(0, 0, 0, 0.06)",
|
|
2976
|
-
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
2977
|
-
fontSize: "0.85rem"
|
|
2978
|
-
},
|
|
2979
|
-
".cm-draftly-code-header-left": {
|
|
2980
|
-
display: "flex",
|
|
2981
|
-
alignItems: "center",
|
|
2982
|
-
gap: "0.5rem"
|
|
2983
|
-
},
|
|
2984
|
-
".cm-draftly-code-header-title": {
|
|
2985
|
-
color: "var(--color-text, inherit)",
|
|
2986
|
-
fontWeight: "500"
|
|
2987
|
-
},
|
|
2988
|
-
".cm-draftly-code-header-lang": {
|
|
2989
|
-
color: "#6a737d",
|
|
2990
|
-
opacity: "0.8"
|
|
2991
|
-
},
|
|
2992
|
-
".cm-draftly-code-header-right": {
|
|
2993
|
-
display: "flex",
|
|
2994
|
-
alignItems: "center",
|
|
2995
|
-
gap: "0.5rem"
|
|
2996
|
-
},
|
|
2997
|
-
".cm-draftly-code-copy-btn": {
|
|
2998
|
-
display: "flex",
|
|
2999
|
-
alignItems: "center",
|
|
3000
|
-
justifyContent: "center",
|
|
3001
|
-
padding: "0.25rem",
|
|
3002
|
-
backgroundColor: "transparent",
|
|
3003
|
-
border: "none",
|
|
3004
|
-
borderRadius: "4px",
|
|
3005
|
-
cursor: "pointer",
|
|
3006
|
-
color: "#6a737d",
|
|
3007
|
-
transition: "color 0.2s, background-color 0.2s"
|
|
3008
|
-
},
|
|
3009
|
-
".cm-draftly-code-copy-btn:hover": {
|
|
3010
|
-
backgroundColor: "rgba(0, 0, 0, 0.1)",
|
|
3011
|
-
color: "var(--color-text, inherit)"
|
|
3012
|
-
},
|
|
3013
|
-
".cm-draftly-code-copy-btn.copied": {
|
|
3014
|
-
color: "#22c55e"
|
|
3015
|
-
},
|
|
3016
|
-
// Caption (below code block)
|
|
3017
|
-
".cm-draftly-code-block-has-caption": {
|
|
3018
|
-
padding: "0 !important",
|
|
3019
|
-
paddingTop: "0.5rem !important"
|
|
3020
|
-
},
|
|
3021
|
-
".cm-draftly-code-caption": {
|
|
3022
|
-
textAlign: "center",
|
|
3023
|
-
fontSize: "0.85rem",
|
|
3024
|
-
color: "#6a737d",
|
|
3025
|
-
fontStyle: "italic",
|
|
3026
|
-
padding: "0.25rem 1rem",
|
|
3027
|
-
backgroundColor: "rgba(0, 0, 0, 0.06)"
|
|
3028
|
-
},
|
|
3029
|
-
// Last line of code block
|
|
3030
|
-
".cm-draftly-code-block-line-end": {
|
|
3031
|
-
borderBottomLeftRadius: "var(--radius)",
|
|
3032
|
-
borderBottomRightRadius: "var(--radius)",
|
|
3033
|
-
borderBottom: "1px solid var(--color-border)",
|
|
3034
|
-
paddingTop: "0.5rem !important"
|
|
3035
|
-
},
|
|
3036
|
-
".cm-draftly-code-block-line-end br": {
|
|
3037
|
-
display: "none"
|
|
3038
|
-
},
|
|
3039
|
-
// Fence markers (```)
|
|
3040
|
-
".cm-draftly-code-fence": {
|
|
3041
|
-
color: "#6a737d",
|
|
3042
|
-
fontFamily: "var(--font-jetbrains-mono, monospace)"
|
|
3043
|
-
},
|
|
3044
|
-
// Line numbers
|
|
3045
|
-
".cm-draftly-code-line-numbered": {
|
|
3046
|
-
paddingLeft: "calc(var(--line-num-width, 2ch) + 1rem) !important",
|
|
3047
|
-
position: "relative"
|
|
3048
|
-
},
|
|
3049
|
-
".cm-draftly-code-line-numbered::before": {
|
|
3050
|
-
content: "attr(data-line-num)",
|
|
3051
|
-
position: "absolute",
|
|
3052
|
-
left: "0.5rem",
|
|
3053
|
-
top: "0.2rem",
|
|
3054
|
-
width: "var(--line-num-width, 2ch)",
|
|
3055
|
-
textAlign: "right",
|
|
3056
|
-
color: "#6a737d",
|
|
3057
|
-
opacity: "0.6",
|
|
3058
|
-
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
3059
|
-
fontSize: "0.85rem",
|
|
3060
|
-
userSelect: "none"
|
|
3061
|
-
},
|
|
3062
|
-
// Preview: code lines (need block display for full-width highlights)
|
|
3063
|
-
".cm-draftly-code-line": {
|
|
3064
|
-
display: "block",
|
|
3065
|
-
position: "relative",
|
|
3066
|
-
paddingLeft: "1rem",
|
|
3067
|
-
paddingRight: "1rem",
|
|
3068
|
-
lineHeight: "1.5",
|
|
3069
|
-
borderLeft: "3px solid transparent"
|
|
3070
|
-
},
|
|
3071
|
-
// Line highlight
|
|
3072
|
-
".cm-draftly-code-line-highlight": {
|
|
3073
|
-
backgroundColor: "rgba(255, 220, 100, 0.2) !important",
|
|
3074
|
-
borderLeft: "3px solid #f0b429 !important"
|
|
3075
|
-
},
|
|
3076
|
-
// Text highlight
|
|
3077
|
-
".cm-draftly-code-text-highlight": {
|
|
3078
|
-
backgroundColor: "rgba(255, 220, 100, 0.4)",
|
|
3079
|
-
borderRadius: "2px",
|
|
3080
|
-
padding: "0.1rem 0"
|
|
3081
|
-
},
|
|
3082
|
-
// Preview: container wrapper
|
|
3083
|
-
".cm-draftly-code-container": {
|
|
3084
|
-
margin: "1rem 0",
|
|
3085
|
-
borderRadius: "var(--radius)",
|
|
3086
|
-
overflow: "hidden",
|
|
3087
|
-
border: "1px solid var(--color-border)"
|
|
3088
|
-
},
|
|
3089
|
-
// Preview: header inside container
|
|
3090
|
-
".cm-draftly-code-container .cm-draftly-code-header": {
|
|
3091
|
-
borderRadius: "0",
|
|
3092
|
-
border: "none",
|
|
3093
|
-
borderBottom: "1px solid var(--color-border)"
|
|
3094
|
-
},
|
|
3095
|
-
// Preview: code block inside container
|
|
3096
|
-
".cm-draftly-code-container .cm-draftly-code-block": {
|
|
3097
|
-
margin: "0",
|
|
3098
|
-
borderRadius: "0",
|
|
3099
|
-
border: "none",
|
|
3100
|
-
whiteSpace: "pre-wrap"
|
|
3101
|
-
},
|
|
3102
|
-
// Preview: caption inside container
|
|
3103
|
-
".cm-draftly-code-container .cm-draftly-code-caption": {
|
|
3104
|
-
borderTop: "1px solid var(--color-border)"
|
|
3105
|
-
},
|
|
3106
|
-
// Preview: standalone code block (not in container)
|
|
3107
|
-
".cm-draftly-code-block": {
|
|
3108
|
-
fontFamily: "var(--font-jetbrains-mono, monospace)",
|
|
3109
|
-
fontSize: "0.9rem",
|
|
3110
|
-
backgroundColor: "rgba(0, 0, 0, 0.03)",
|
|
3111
|
-
padding: "1rem",
|
|
3112
|
-
overflow: "auto",
|
|
3113
|
-
position: "relative",
|
|
3114
|
-
borderRadius: "var(--radius)",
|
|
3115
|
-
border: "1px solid var(--color-border)"
|
|
3116
|
-
},
|
|
3117
|
-
// Preview: code block with header (remove top radius)
|
|
3118
|
-
".cm-draftly-code-block.cm-draftly-code-block-has-header": {
|
|
3119
|
-
borderTopLeftRadius: "0",
|
|
3120
|
-
borderTopRightRadius: "0",
|
|
3121
|
-
borderTop: "none",
|
|
3122
|
-
margin: "0",
|
|
3123
|
-
paddingTop: "0.5rem !important"
|
|
3124
|
-
},
|
|
3125
|
-
// Preview: code block with caption (remove bottom radius)
|
|
3126
|
-
".cm-draftly-code-block.cm-draftly-code-block-has-caption": {
|
|
3127
|
-
borderBottomLeftRadius: "0",
|
|
3128
|
-
borderBottomRightRadius: "0",
|
|
3129
|
-
borderBottom: "none",
|
|
3130
|
-
paddingBottom: "0.5rem !important"
|
|
3131
|
-
}
|
|
3132
|
-
},
|
|
3133
|
-
dark: {
|
|
3134
|
-
".cm-draftly-code-inline": {
|
|
3135
|
-
backgroundColor: "rgba(255, 255, 255, 0.1)"
|
|
3136
|
-
},
|
|
3137
|
-
".cm-draftly-code-block-line": {
|
|
3138
|
-
backgroundColor: "rgba(255, 255, 255, 0.05)"
|
|
3139
|
-
},
|
|
3140
|
-
".cm-draftly-code-fence": {
|
|
3141
|
-
color: "#8b949e"
|
|
3142
|
-
},
|
|
3143
|
-
".cm-draftly-code-block": {
|
|
3144
|
-
backgroundColor: "rgba(255, 255, 255, 0.05)"
|
|
3145
|
-
},
|
|
3146
|
-
".cm-draftly-code-header": {
|
|
3147
|
-
backgroundColor: "rgba(255, 255, 255, 0.08)"
|
|
3148
|
-
},
|
|
3149
|
-
".cm-draftly-code-header-lang": {
|
|
3150
|
-
color: "#8b949e"
|
|
3151
|
-
},
|
|
3152
|
-
".cm-draftly-code-copy-btn": {
|
|
3153
|
-
color: "#8b949e"
|
|
3154
|
-
},
|
|
3155
|
-
".cm-draftly-code-copy-btn:hover": {
|
|
3156
|
-
backgroundColor: "rgba(255, 255, 255, 0.1)"
|
|
3157
|
-
},
|
|
3158
|
-
".cm-draftly-code-caption": {
|
|
3159
|
-
backgroundColor: "rgba(255, 255, 255, 0.05)"
|
|
3160
|
-
},
|
|
3161
|
-
".cm-draftly-code-line-numbered::before": {
|
|
3162
|
-
color: "#8b949e"
|
|
3163
|
-
},
|
|
3164
|
-
".cm-draftly-code-line-highlight": {
|
|
3165
|
-
backgroundColor: "rgba(255, 220, 100, 0.15) !important",
|
|
3166
|
-
borderLeft: "3px solid #d9a520 !important"
|
|
3167
|
-
},
|
|
3168
|
-
".cm-draftly-code-text-highlight": {
|
|
3169
|
-
backgroundColor: "rgba(255, 220, 100, 0.3)"
|
|
3170
|
-
}
|
|
3171
|
-
}
|
|
3172
|
-
});
|
|
3173
4357
|
var quoteMarkDecorations = {
|
|
3174
4358
|
/** Decoration for the > marker */
|
|
3175
4359
|
"quote-mark": Decoration.replace({}),
|
|
@@ -3337,6 +4521,103 @@ var theme12 = createTheme({
|
|
|
3337
4521
|
}
|
|
3338
4522
|
}
|
|
3339
4523
|
});
|
|
4524
|
+
function shortcodeToEmoji(raw) {
|
|
4525
|
+
const rendered = emoji.emojify(raw);
|
|
4526
|
+
return rendered !== raw ? rendered : null;
|
|
4527
|
+
}
|
|
4528
|
+
var EmojiWidget = class extends WidgetType {
|
|
4529
|
+
constructor(rendered) {
|
|
4530
|
+
super();
|
|
4531
|
+
this.rendered = rendered;
|
|
4532
|
+
}
|
|
4533
|
+
eq(other) {
|
|
4534
|
+
return other.rendered === this.rendered;
|
|
4535
|
+
}
|
|
4536
|
+
toDOM() {
|
|
4537
|
+
const span = document.createElement("span");
|
|
4538
|
+
span.className = "cm-draftly-emoji";
|
|
4539
|
+
span.textContent = this.rendered;
|
|
4540
|
+
return span;
|
|
4541
|
+
}
|
|
4542
|
+
ignoreEvent() {
|
|
4543
|
+
return false;
|
|
4544
|
+
}
|
|
4545
|
+
};
|
|
4546
|
+
var emojiMarkDecorations = {
|
|
4547
|
+
"emoji-source": Decoration.mark({ class: "cm-draftly-emoji-source" })
|
|
4548
|
+
};
|
|
4549
|
+
var EmojiPlugin = class extends DecorationPlugin {
|
|
4550
|
+
name = "emoji";
|
|
4551
|
+
version = "1.0.0";
|
|
4552
|
+
decorationPriority = 20;
|
|
4553
|
+
requiredNodes = ["Emoji", "EmojiMark"];
|
|
4554
|
+
constructor() {
|
|
4555
|
+
super();
|
|
4556
|
+
}
|
|
4557
|
+
/**
|
|
4558
|
+
* Plugin theme
|
|
4559
|
+
*/
|
|
4560
|
+
get theme() {
|
|
4561
|
+
return theme13;
|
|
4562
|
+
}
|
|
4563
|
+
/**
|
|
4564
|
+
* Build emoji decorations by iterating the syntax tree
|
|
4565
|
+
*/
|
|
4566
|
+
buildDecorations(ctx) {
|
|
4567
|
+
const { view, decorations } = ctx;
|
|
4568
|
+
const tree = syntaxTree(view.state);
|
|
4569
|
+
tree.iterate({
|
|
4570
|
+
enter: (node) => {
|
|
4571
|
+
const { from, to, name } = node;
|
|
4572
|
+
if (name !== "Emoji") {
|
|
4573
|
+
return;
|
|
4574
|
+
}
|
|
4575
|
+
const raw = view.state.sliceDoc(from, to);
|
|
4576
|
+
const rendered = shortcodeToEmoji(raw);
|
|
4577
|
+
if (!rendered) {
|
|
4578
|
+
return;
|
|
4579
|
+
}
|
|
4580
|
+
const cursorInNode = ctx.selectionOverlapsRange(from, to);
|
|
4581
|
+
if (cursorInNode) {
|
|
4582
|
+
decorations.push(emojiMarkDecorations["emoji-source"].range(from, to));
|
|
4583
|
+
return;
|
|
4584
|
+
}
|
|
4585
|
+
decorations.push(
|
|
4586
|
+
Decoration.replace({
|
|
4587
|
+
widget: new EmojiWidget(rendered)
|
|
4588
|
+
}).range(from, to)
|
|
4589
|
+
);
|
|
4590
|
+
}
|
|
4591
|
+
});
|
|
4592
|
+
}
|
|
4593
|
+
renderToHTML(node, children, ctx) {
|
|
4594
|
+
if (node.name === "EmojiMark") {
|
|
4595
|
+
return "";
|
|
4596
|
+
}
|
|
4597
|
+
if (node.name !== "Emoji") {
|
|
4598
|
+
return null;
|
|
4599
|
+
}
|
|
4600
|
+
const raw = ctx.sliceDoc(node.from, node.to);
|
|
4601
|
+
const rendered = shortcodeToEmoji(raw);
|
|
4602
|
+
if (!rendered) {
|
|
4603
|
+
return `<span class="cm-draftly-emoji-source">${children}</span>`;
|
|
4604
|
+
}
|
|
4605
|
+
return `<span class="cm-draftly-emoji">${rendered}</span>`;
|
|
4606
|
+
}
|
|
4607
|
+
};
|
|
4608
|
+
var theme13 = createTheme({
|
|
4609
|
+
default: {
|
|
4610
|
+
".cm-draftly-emoji": {
|
|
4611
|
+
fontFamily: '"Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Segoe UI Symbol", sans-serif',
|
|
4612
|
+
fontVariantEmoji: "emoji",
|
|
4613
|
+
lineHeight: "1.2"
|
|
4614
|
+
},
|
|
4615
|
+
".cm-draftly-emoji-source": {
|
|
4616
|
+
fontFamily: "inherit",
|
|
4617
|
+
lineHeight: "inherit"
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
});
|
|
3340
4621
|
|
|
3341
4622
|
// src/plugins/index.ts
|
|
3342
4623
|
var essentialPlugins = [
|
|
@@ -3345,16 +4626,18 @@ var essentialPlugins = [
|
|
|
3345
4626
|
new InlinePlugin(),
|
|
3346
4627
|
new LinkPlugin(),
|
|
3347
4628
|
new ListPlugin(),
|
|
4629
|
+
new TablePlugin(),
|
|
3348
4630
|
new HTMLPlugin(),
|
|
3349
4631
|
new ImagePlugin(),
|
|
3350
4632
|
new MathPlugin(),
|
|
3351
4633
|
new MermaidPlugin(),
|
|
3352
4634
|
new CodePlugin(),
|
|
3353
4635
|
new QuotePlugin(),
|
|
3354
|
-
new HRPlugin()
|
|
4636
|
+
new HRPlugin(),
|
|
4637
|
+
new EmojiPlugin()
|
|
3355
4638
|
];
|
|
3356
4639
|
var allPlugins = [...essentialPlugins];
|
|
3357
4640
|
|
|
3358
|
-
export { CodePlugin, HRPlugin, HTMLPlugin, HeadingPlugin, ImagePlugin, InlinePlugin, LinkPlugin, ListPlugin, MathPlugin, MermaidPlugin, ParagraphPlugin, QuotePlugin, allPlugins, essentialPlugins };
|
|
3359
|
-
//# sourceMappingURL=chunk-
|
|
3360
|
-
//# sourceMappingURL=chunk-
|
|
4641
|
+
export { CodePlugin, EmojiPlugin, HRPlugin, HTMLPlugin, HeadingPlugin, ImagePlugin, InlinePlugin, LinkPlugin, ListPlugin, MathPlugin, MermaidPlugin, ParagraphPlugin, QuotePlugin, TablePlugin, allPlugins, essentialPlugins };
|
|
4642
|
+
//# sourceMappingURL=chunk-L2XSK57Y.js.map
|
|
4643
|
+
//# sourceMappingURL=chunk-L2XSK57Y.js.map
|