draftly 2.0.0 → 2.1.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/dist/{chunk-BWJLMREN.cjs → chunk-EQUQHE2E.cjs} +26 -24
- package/dist/chunk-EQUQHE2E.cjs.map +1 -0
- package/dist/{chunk-L2XSK57Y.js → chunk-NRPI5O6Y.js} +1267 -551
- package/dist/chunk-NRPI5O6Y.js.map +1 -0
- package/dist/{chunk-W5ALMXG2.cjs → chunk-OMFUE4AQ.cjs} +1304 -588
- package/dist/chunk-OMFUE4AQ.cjs.map +1 -0
- package/dist/{chunk-EEHILRG5.js → chunk-TD3L5C45.js} +13 -11
- package/dist/chunk-TD3L5C45.js.map +1 -0
- package/dist/{chunk-ZUI3GI3W.js → chunk-UCHBDJ4R.js} +22 -20
- package/dist/chunk-UCHBDJ4R.js.map +1 -0
- package/dist/{chunk-TBVZEK2H.cjs → chunk-W75QUUQC.cjs} +13 -11
- package/dist/chunk-W75QUUQC.cjs.map +1 -0
- package/dist/editor/index.cjs +16 -16
- package/dist/editor/index.js +2 -2
- package/dist/index.cjs +33 -33
- package/dist/index.js +3 -3
- package/dist/plugins/index.cjs +19 -19
- package/dist/plugins/index.d.cts +77 -42
- package/dist/plugins/index.d.ts +77 -42
- package/dist/plugins/index.js +3 -3
- package/dist/preview/index.cjs +1 -1
- package/dist/preview/index.js +1 -1
- package/package.json +1 -1
- package/src/editor/draftly.ts +29 -27
- package/src/editor/utils.ts +13 -11
- package/src/plugins/table-plugin.ts +1759 -900
- package/dist/chunk-BWJLMREN.cjs.map +0 -1
- package/dist/chunk-EEHILRG5.js.map +0 -1
- package/dist/chunk-L2XSK57Y.js.map +0 -1
- package/dist/chunk-TBVZEK2H.cjs.map +0 -1
- package/dist/chunk-W5ALMXG2.cjs.map +0 -1
- package/dist/chunk-ZUI3GI3W.js.map +0 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { DraftlyPlugin, DecorationPlugin } from './chunk-
|
|
1
|
+
import { DraftlyPlugin, DecorationPlugin } from './chunk-UCHBDJ4R.js';
|
|
2
2
|
import { createWrapSelectionInputHandler } from './chunk-JF3WXXMJ.js';
|
|
3
3
|
import { PreviewRenderer } from './chunk-I563H35S.js';
|
|
4
|
-
import { createTheme, toggleMarkdownStyle } from './chunk-
|
|
5
|
-
import { Decoration, WidgetType } from '@codemirror/view';
|
|
4
|
+
import { createTheme, toggleMarkdownStyle } from './chunk-TD3L5C45.js';
|
|
5
|
+
import { Decoration, BlockWrapper, keymap, EditorView, WidgetType } from '@codemirror/view';
|
|
6
6
|
import { syntaxTree, LanguageDescription } from '@codemirror/language';
|
|
7
7
|
import { tags, highlightCode } from '@lezer/highlight';
|
|
8
|
+
import { Annotation, Prec, RangeSet } from '@codemirror/state';
|
|
9
|
+
import { Table } from '@lezer/markdown';
|
|
8
10
|
import DOMPurify from 'dompurify';
|
|
9
11
|
import katex from 'katex';
|
|
10
12
|
import katexCss from 'katex/dist/katex.min.css?raw';
|
|
@@ -1124,6 +1126,130 @@ var theme5 = createTheme({
|
|
|
1124
1126
|
}
|
|
1125
1127
|
}
|
|
1126
1128
|
});
|
|
1129
|
+
var BREAK_TAG = "<br />";
|
|
1130
|
+
var BREAK_TAG_REGEX = /<br\s*\/?>/gi;
|
|
1131
|
+
var DELIMITER_CELL_PATTERN = /^:?-{3,}:?$/;
|
|
1132
|
+
var TABLE_SUB_NODE_NAMES = /* @__PURE__ */ new Set(["TableHeader", "TableDelimiter", "TableRow", "TableCell"]);
|
|
1133
|
+
var TABLE_TEMPLATE = {
|
|
1134
|
+
headers: ["Header 1", "Header 2", "Header 3"],
|
|
1135
|
+
alignments: ["left", "left", "left"],
|
|
1136
|
+
rows: [["", "", ""]]
|
|
1137
|
+
};
|
|
1138
|
+
var normalizeAnnotation = Annotation.define();
|
|
1139
|
+
var repairSelectionAnnotation = Annotation.define();
|
|
1140
|
+
var pipeReplace = Decoration.replace({});
|
|
1141
|
+
var delimiterReplace = Decoration.replace({});
|
|
1142
|
+
var tableBlockWrapper = BlockWrapper.create({
|
|
1143
|
+
tagName: "div",
|
|
1144
|
+
attributes: { class: "cm-draftly-table-wrapper" }
|
|
1145
|
+
});
|
|
1146
|
+
var TableBreakWidget = class extends WidgetType {
|
|
1147
|
+
/** Reuses the same widget instance for identical break markers. */
|
|
1148
|
+
eq() {
|
|
1149
|
+
return true;
|
|
1150
|
+
}
|
|
1151
|
+
/** Renders an inline `<br />` placeholder inside a table cell. */
|
|
1152
|
+
toDOM() {
|
|
1153
|
+
const span = document.createElement("span");
|
|
1154
|
+
span.className = "cm-draftly-table-break";
|
|
1155
|
+
span.setAttribute("aria-label", "line break");
|
|
1156
|
+
span.appendChild(document.createElement("br"));
|
|
1157
|
+
return span;
|
|
1158
|
+
}
|
|
1159
|
+
/** Allows the editor to observe events on the rendered break widget. */
|
|
1160
|
+
ignoreEvent() {
|
|
1161
|
+
return false;
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
var TableControlsWidget = class extends WidgetType {
|
|
1165
|
+
constructor(onAddRow, onAddColumn) {
|
|
1166
|
+
super();
|
|
1167
|
+
this.onAddRow = onAddRow;
|
|
1168
|
+
this.onAddColumn = onAddColumn;
|
|
1169
|
+
}
|
|
1170
|
+
/** Forces the control widget to be recreated so handlers stay current. */
|
|
1171
|
+
eq() {
|
|
1172
|
+
return false;
|
|
1173
|
+
}
|
|
1174
|
+
/** Renders the hover controls used to append rows and columns. */
|
|
1175
|
+
toDOM(view) {
|
|
1176
|
+
const anchor = document.createElement("span");
|
|
1177
|
+
anchor.className = "cm-draftly-table-controls-anchor";
|
|
1178
|
+
anchor.setAttribute("aria-hidden", "true");
|
|
1179
|
+
const rightButton = this.createButton("Add column", "cm-draftly-table-control cm-draftly-table-control-column");
|
|
1180
|
+
rightButton.addEventListener("mousedown", (event) => {
|
|
1181
|
+
event.preventDefault();
|
|
1182
|
+
event.stopPropagation();
|
|
1183
|
+
this.onAddColumn(view);
|
|
1184
|
+
});
|
|
1185
|
+
const bottomButton = this.createButton("Add row", "cm-draftly-table-control cm-draftly-table-control-row");
|
|
1186
|
+
bottomButton.addEventListener("mousedown", (event) => {
|
|
1187
|
+
event.preventDefault();
|
|
1188
|
+
event.stopPropagation();
|
|
1189
|
+
this.onAddRow(view);
|
|
1190
|
+
});
|
|
1191
|
+
anchor.append(rightButton, bottomButton);
|
|
1192
|
+
return anchor;
|
|
1193
|
+
}
|
|
1194
|
+
/** Lets button events bubble through the widget. */
|
|
1195
|
+
ignoreEvent() {
|
|
1196
|
+
return false;
|
|
1197
|
+
}
|
|
1198
|
+
/** Builds a single control button with the provided label and class. */
|
|
1199
|
+
createButton(label, className) {
|
|
1200
|
+
const button = document.createElement("button");
|
|
1201
|
+
button.type = "button";
|
|
1202
|
+
button.className = className;
|
|
1203
|
+
button.setAttribute("tabindex", "-1");
|
|
1204
|
+
button.setAttribute("aria-label", label);
|
|
1205
|
+
button.textContent = "+";
|
|
1206
|
+
return button;
|
|
1207
|
+
}
|
|
1208
|
+
};
|
|
1209
|
+
function isEscaped(text, index) {
|
|
1210
|
+
let slashCount = 0;
|
|
1211
|
+
for (let i = index - 1; i >= 0 && text[i] === "\\"; i--) {
|
|
1212
|
+
slashCount++;
|
|
1213
|
+
}
|
|
1214
|
+
return slashCount % 2 === 1;
|
|
1215
|
+
}
|
|
1216
|
+
function getPipePositions(lineText) {
|
|
1217
|
+
const positions = [];
|
|
1218
|
+
for (let index = 0; index < lineText.length; index++) {
|
|
1219
|
+
if (lineText[index] === "|" && !isEscaped(lineText, index)) {
|
|
1220
|
+
positions.push(index);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
return positions;
|
|
1224
|
+
}
|
|
1225
|
+
function splitTableLine(lineText) {
|
|
1226
|
+
const cells = [];
|
|
1227
|
+
const trimmed = lineText.trim();
|
|
1228
|
+
if (!trimmed.includes("|")) {
|
|
1229
|
+
return [trimmed];
|
|
1230
|
+
}
|
|
1231
|
+
let current = "";
|
|
1232
|
+
for (let index = 0; index < trimmed.length; index++) {
|
|
1233
|
+
const char = trimmed[index];
|
|
1234
|
+
if (char === "|" && !isEscaped(trimmed, index)) {
|
|
1235
|
+
cells.push(current);
|
|
1236
|
+
current = "";
|
|
1237
|
+
continue;
|
|
1238
|
+
}
|
|
1239
|
+
current += char;
|
|
1240
|
+
}
|
|
1241
|
+
cells.push(current);
|
|
1242
|
+
if (trimmed.startsWith("|")) {
|
|
1243
|
+
cells.shift();
|
|
1244
|
+
}
|
|
1245
|
+
if (trimmed.endsWith("|")) {
|
|
1246
|
+
cells.pop();
|
|
1247
|
+
}
|
|
1248
|
+
return cells;
|
|
1249
|
+
}
|
|
1250
|
+
function isTableRowLine(lineText) {
|
|
1251
|
+
return getPipePositions(lineText.trim()).length > 0;
|
|
1252
|
+
}
|
|
1127
1253
|
function parseAlignment(cell) {
|
|
1128
1254
|
const trimmed = cell.trim();
|
|
1129
1255
|
const left = trimmed.startsWith(":");
|
|
@@ -1132,642 +1258,1232 @@ function parseAlignment(cell) {
|
|
|
1132
1258
|
if (right) return "right";
|
|
1133
1259
|
return "left";
|
|
1134
1260
|
}
|
|
1135
|
-
function
|
|
1136
|
-
const
|
|
1137
|
-
if (
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1261
|
+
function parseDelimiterAlignments(lineText) {
|
|
1262
|
+
const cells = splitTableLine(lineText).map((cell) => cell.trim());
|
|
1263
|
+
if (cells.length === 0 || !cells.every((cell) => DELIMITER_CELL_PATTERN.test(cell))) {
|
|
1264
|
+
return null;
|
|
1265
|
+
}
|
|
1266
|
+
return cells.map(parseAlignment);
|
|
1267
|
+
}
|
|
1268
|
+
function splitTableAndTrailingMarkdown(markdown) {
|
|
1269
|
+
const lines = markdown.split("\n");
|
|
1270
|
+
if (lines.length < 2) {
|
|
1271
|
+
return { tableMarkdown: markdown, trailingMarkdown: "" };
|
|
1272
|
+
}
|
|
1273
|
+
const headerLine = lines[0] || "";
|
|
1274
|
+
const delimiterLine = lines[1] || "";
|
|
1275
|
+
if (!isTableRowLine(headerLine) || !parseDelimiterAlignments(delimiterLine)) {
|
|
1276
|
+
return { tableMarkdown: markdown, trailingMarkdown: "" };
|
|
1277
|
+
}
|
|
1278
|
+
let endIndex = 1;
|
|
1279
|
+
for (let index = 2; index < lines.length; index++) {
|
|
1280
|
+
if (!isTableRowLine(lines[index] || "")) {
|
|
1281
|
+
break;
|
|
1282
|
+
}
|
|
1283
|
+
endIndex = index;
|
|
1284
|
+
}
|
|
1285
|
+
return {
|
|
1286
|
+
tableMarkdown: lines.slice(0, endIndex + 1).join("\n"),
|
|
1287
|
+
trailingMarkdown: lines.slice(endIndex + 1).join("\n")
|
|
1143
1288
|
};
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
for (let
|
|
1151
|
-
|
|
1289
|
+
}
|
|
1290
|
+
function canonicalizeBreakTags(text) {
|
|
1291
|
+
return text.replace(BREAK_TAG_REGEX, BREAK_TAG);
|
|
1292
|
+
}
|
|
1293
|
+
function escapeUnescapedPipes(text) {
|
|
1294
|
+
let result = "";
|
|
1295
|
+
for (let index = 0; index < text.length; index++) {
|
|
1296
|
+
const char = text[index];
|
|
1297
|
+
if (char === "|" && !isEscaped(text, index)) {
|
|
1298
|
+
result += "\\|";
|
|
1299
|
+
continue;
|
|
1300
|
+
}
|
|
1301
|
+
result += char;
|
|
1152
1302
|
}
|
|
1153
|
-
return
|
|
1303
|
+
return result;
|
|
1304
|
+
}
|
|
1305
|
+
function normalizeCellContent(text) {
|
|
1306
|
+
const normalizedBreaks = canonicalizeBreakTags(text.trim());
|
|
1307
|
+
if (!normalizedBreaks) {
|
|
1308
|
+
return "";
|
|
1309
|
+
}
|
|
1310
|
+
const parts = normalizedBreaks.split(BREAK_TAG_REGEX).map((part) => escapeUnescapedPipes(part.trim()));
|
|
1311
|
+
if (parts.length === 1) {
|
|
1312
|
+
return parts[0] || "";
|
|
1313
|
+
}
|
|
1314
|
+
return parts.join(` ${BREAK_TAG} `).trim();
|
|
1154
1315
|
}
|
|
1155
|
-
function
|
|
1156
|
-
|
|
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() === "");
|
|
1316
|
+
function renderWidth(text) {
|
|
1317
|
+
return canonicalizeBreakTags(text).replace(BREAK_TAG, " ").replace(/\\\|/g, "|").length;
|
|
1162
1318
|
}
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1319
|
+
function padCell(text, width, alignment) {
|
|
1320
|
+
const safeWidth = Math.max(width, renderWidth(text));
|
|
1321
|
+
const difference = safeWidth - renderWidth(text);
|
|
1322
|
+
if (difference <= 0) {
|
|
1323
|
+
return text;
|
|
1166
1324
|
}
|
|
1167
|
-
|
|
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];
|
|
1325
|
+
if (alignment === "right") {
|
|
1326
|
+
return " ".repeat(difference) + text;
|
|
1178
1327
|
}
|
|
1179
|
-
|
|
1328
|
+
if (alignment === "center") {
|
|
1329
|
+
const left = Math.floor(difference / 2);
|
|
1330
|
+
const right = difference - left;
|
|
1331
|
+
return " ".repeat(left) + text + " ".repeat(right);
|
|
1332
|
+
}
|
|
1333
|
+
return text + " ".repeat(difference);
|
|
1334
|
+
}
|
|
1335
|
+
function delimiterCell(width, alignment) {
|
|
1336
|
+
const hyphenCount = Math.max(width, 3);
|
|
1337
|
+
if (alignment === "center") {
|
|
1338
|
+
return ":" + "-".repeat(Math.max(1, hyphenCount - 2)) + ":";
|
|
1339
|
+
}
|
|
1340
|
+
if (alignment === "right") {
|
|
1341
|
+
return "-".repeat(Math.max(2, hyphenCount - 1)) + ":";
|
|
1342
|
+
}
|
|
1343
|
+
return "-".repeat(hyphenCount);
|
|
1344
|
+
}
|
|
1345
|
+
function parseTableMarkdown(markdown) {
|
|
1346
|
+
const { tableMarkdown } = splitTableAndTrailingMarkdown(markdown);
|
|
1347
|
+
const lines = tableMarkdown.split("\n");
|
|
1348
|
+
if (lines.length < 2) {
|
|
1349
|
+
return null;
|
|
1350
|
+
}
|
|
1351
|
+
const headers = splitTableLine(lines[0] || "").map((cell) => cell.trim());
|
|
1352
|
+
const alignments = parseDelimiterAlignments(lines[1] || "");
|
|
1353
|
+
if (!alignments) {
|
|
1354
|
+
return null;
|
|
1355
|
+
}
|
|
1356
|
+
const rows = lines.slice(2).filter((line) => isTableRowLine(line)).map((line) => splitTableLine(line).map((cell) => cell.trim()));
|
|
1357
|
+
return { headers, alignments, rows };
|
|
1180
1358
|
}
|
|
1181
|
-
function
|
|
1182
|
-
|
|
1359
|
+
function normalizeParsedTable(parsed) {
|
|
1360
|
+
const columnCount = Math.max(
|
|
1361
|
+
parsed.headers.length,
|
|
1362
|
+
parsed.alignments.length,
|
|
1363
|
+
...parsed.rows.map((row) => row.length),
|
|
1364
|
+
1
|
|
1365
|
+
);
|
|
1366
|
+
const headers = Array.from({ length: columnCount }, (_, index) => normalizeCellContent(parsed.headers[index] || ""));
|
|
1367
|
+
const alignments = Array.from({ length: columnCount }, (_, index) => parsed.alignments[index] || "left");
|
|
1368
|
+
const rows = parsed.rows.map(
|
|
1369
|
+
(row) => Array.from({ length: columnCount }, (_, index) => normalizeCellContent(row[index] || ""))
|
|
1370
|
+
);
|
|
1371
|
+
return { headers, alignments, rows };
|
|
1372
|
+
}
|
|
1373
|
+
function formatTableMarkdown(parsed) {
|
|
1374
|
+
const normalized = normalizeParsedTable(parsed);
|
|
1375
|
+
const widths = normalized.headers.map(
|
|
1376
|
+
(header, index) => Math.max(renderWidth(header), ...normalized.rows.map((row) => renderWidth(row[index] || "")), 3)
|
|
1377
|
+
);
|
|
1378
|
+
const formatRow = (cells) => `| ${cells.map((cell, index) => padCell(cell, widths[index] || 3, normalized.alignments[index] || "left")).join(" | ")} |`;
|
|
1379
|
+
const headerLine = formatRow(normalized.headers);
|
|
1380
|
+
const delimiterLine = `| ${normalized.alignments.map((alignment, index) => delimiterCell(widths[index] || 3, alignment)).join(" | ")} |`;
|
|
1381
|
+
const bodyLines = normalized.rows.map((row) => formatRow(row));
|
|
1382
|
+
return [headerLine, delimiterLine, ...bodyLines].join("\n");
|
|
1383
|
+
}
|
|
1384
|
+
function buildEmptyRow(columnCount) {
|
|
1385
|
+
return Array.from({ length: columnCount }, () => "");
|
|
1386
|
+
}
|
|
1387
|
+
function createPreviewRenderer(markdown, config) {
|
|
1388
|
+
const plugins = (config?.plugins || []).filter((plugin) => plugin.name !== "paragraph");
|
|
1389
|
+
return new PreviewRenderer(markdown, plugins, config?.markdown || [], config?.theme || "auto" /* AUTO */, true);
|
|
1390
|
+
}
|
|
1391
|
+
function stripSingleParagraph(html) {
|
|
1392
|
+
const trimmed = html.trim();
|
|
1393
|
+
const match = trimmed.match(/^<p\b[^>]*>([\s\S]*)<\/p>$/i);
|
|
1394
|
+
return match?.[1] || trimmed;
|
|
1183
1395
|
}
|
|
1184
|
-
function
|
|
1185
|
-
|
|
1396
|
+
async function renderCellToHtml(text, config) {
|
|
1397
|
+
if (!text.trim()) {
|
|
1398
|
+
return " ";
|
|
1399
|
+
}
|
|
1400
|
+
return stripSingleParagraph(await createPreviewRenderer(text, config).render());
|
|
1186
1401
|
}
|
|
1187
1402
|
async function renderTableToHtml(parsed, config) {
|
|
1188
|
-
const
|
|
1189
|
-
let html = '<div class="cm-draftly-table-widget">';
|
|
1190
|
-
html += '<
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
const
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
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>`;
|
|
1403
|
+
const normalized = normalizeParsedTable(parsed);
|
|
1404
|
+
let html = '<div class="cm-draftly-table-widget"><table class="cm-draftly-table cm-draftly-table-preview">';
|
|
1405
|
+
html += '<thead><tr class="cm-draftly-table-row cm-draftly-table-header-row">';
|
|
1406
|
+
for (let index = 0; index < normalized.headers.length; index++) {
|
|
1407
|
+
const alignment = normalized.alignments[index] || "left";
|
|
1408
|
+
const content = await renderCellToHtml(normalized.headers[index] || "", config);
|
|
1409
|
+
html += `<th class="cm-draftly-table-cell cm-draftly-table-th${alignment === "center" ? " cm-draftly-table-cell-center" : alignment === "right" ? " cm-draftly-table-cell-right" : ""}${index === normalized.headers.length - 1 ? " cm-draftly-table-cell-last" : ""}">${content}</th>`;
|
|
1410
|
+
}
|
|
1411
|
+
html += "</tr></thead><tbody>";
|
|
1412
|
+
for (let rowIndex = 0; rowIndex < normalized.rows.length; rowIndex++) {
|
|
1413
|
+
const row = normalized.rows[rowIndex] || [];
|
|
1414
|
+
html += `<tr class="cm-draftly-table-row cm-draftly-table-body-row${rowIndex % 2 === 1 ? " cm-draftly-table-row-even" : ""}${rowIndex === normalized.rows.length - 1 ? " cm-draftly-table-row-last" : ""}">`;
|
|
1415
|
+
for (let index = 0; index < normalized.headers.length; index++) {
|
|
1416
|
+
const alignment = normalized.alignments[index] || "left";
|
|
1417
|
+
const content = await renderCellToHtml(row[index] || "", config);
|
|
1418
|
+
html += `<td class="cm-draftly-table-cell${alignment === "center" ? " cm-draftly-table-cell-center" : alignment === "right" ? " cm-draftly-table-cell-right" : ""}${index === normalized.headers.length - 1 ? " cm-draftly-table-cell-last" : ""}">${content}</td>`;
|
|
1208
1419
|
}
|
|
1209
1420
|
html += "</tr>";
|
|
1210
1421
|
}
|
|
1211
|
-
html += "</tbody>";
|
|
1212
|
-
html += "</table></div>";
|
|
1422
|
+
html += "</tbody></table></div>";
|
|
1213
1423
|
return html;
|
|
1214
1424
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1425
|
+
function getVisibleBounds(rawCellText) {
|
|
1426
|
+
const leading = rawCellText.length - rawCellText.trimStart().length;
|
|
1427
|
+
const trailing = rawCellText.length - rawCellText.trimEnd().length;
|
|
1428
|
+
const trimmedLength = rawCellText.trim().length;
|
|
1429
|
+
if (trimmedLength === 0) {
|
|
1430
|
+
const placeholderOffset = Math.min(Math.floor(rawCellText.length / 2), Math.max(rawCellText.length - 1, 0));
|
|
1431
|
+
return {
|
|
1432
|
+
startOffset: placeholderOffset,
|
|
1433
|
+
endOffset: Math.min(placeholderOffset + 1, rawCellText.length)
|
|
1434
|
+
};
|
|
1222
1435
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1436
|
+
return {
|
|
1437
|
+
startOffset: leading,
|
|
1438
|
+
endOffset: rawCellText.length - trailing
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
function isBodyRowEmpty(row) {
|
|
1442
|
+
return row.every((cell) => normalizeCellContent(cell.rawText) === "");
|
|
1443
|
+
}
|
|
1444
|
+
function buildTableFromInfo(tableInfo) {
|
|
1445
|
+
return {
|
|
1446
|
+
headers: tableInfo.headerCells.map((cell) => normalizeCellContent(cell.rawText)),
|
|
1447
|
+
alignments: [...tableInfo.alignments],
|
|
1448
|
+
rows: tableInfo.bodyCells.map((row) => row.map((cell) => normalizeCellContent(cell.rawText)))
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
function getRowLineIndex(rowIndex) {
|
|
1452
|
+
return rowIndex === 0 ? 0 : rowIndex + 1;
|
|
1453
|
+
}
|
|
1454
|
+
function getCellAnchorInFormattedTable(formattedTable, rowIndex, columnIndex, offset = 0) {
|
|
1455
|
+
const lines = formattedTable.split("\n");
|
|
1456
|
+
const lineIndex = getRowLineIndex(rowIndex);
|
|
1457
|
+
const lineText = lines[lineIndex] || "";
|
|
1458
|
+
const pipes = getPipePositions(lineText);
|
|
1459
|
+
if (pipes.length < columnIndex + 2) {
|
|
1460
|
+
return formattedTable.length;
|
|
1461
|
+
}
|
|
1462
|
+
const rawFrom = pipes[columnIndex] + 1;
|
|
1463
|
+
const rawTo = pipes[columnIndex + 1];
|
|
1464
|
+
const visible = getVisibleBounds(lineText.slice(rawFrom, rawTo));
|
|
1465
|
+
const lineOffset = lines.slice(0, lineIndex).reduce((sum, line) => sum + line.length + 1, 0);
|
|
1466
|
+
return lineOffset + Math.min(rawFrom + visible.startOffset + offset, rawFrom + Math.max(visible.endOffset - 1, visible.startOffset));
|
|
1467
|
+
}
|
|
1468
|
+
function createTableInsert(state, from, to, tableMarkdown) {
|
|
1469
|
+
let insert = tableMarkdown;
|
|
1470
|
+
let prefixLength = 0;
|
|
1471
|
+
const startLine = state.doc.lineAt(from);
|
|
1472
|
+
if (startLine.number === 1 || state.doc.line(startLine.number - 1).text.trim() !== "") {
|
|
1473
|
+
insert = "\n" + insert;
|
|
1474
|
+
prefixLength = 1;
|
|
1475
|
+
}
|
|
1476
|
+
const endLine = state.doc.lineAt(Math.max(from, to));
|
|
1477
|
+
if (endLine.number === state.doc.lines || state.doc.line(endLine.number + 1).text.trim() !== "") {
|
|
1478
|
+
insert += "\n";
|
|
1479
|
+
}
|
|
1480
|
+
return { from, to, insert, prefixLength };
|
|
1481
|
+
}
|
|
1482
|
+
function readTableInfo(state, nodeFrom, nodeTo) {
|
|
1483
|
+
const startLine = state.doc.lineAt(nodeFrom);
|
|
1484
|
+
const endLine = state.doc.lineAt(nodeTo);
|
|
1485
|
+
const delimiterLineNumber = startLine.number + 1;
|
|
1486
|
+
if (delimiterLineNumber > endLine.number) {
|
|
1487
|
+
return null;
|
|
1225
1488
|
}
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
view.dispatch({
|
|
1269
|
-
selection: { anchor: this.from },
|
|
1270
|
-
scrollIntoView: true
|
|
1489
|
+
const delimiterLine = state.doc.line(delimiterLineNumber);
|
|
1490
|
+
const alignments = parseDelimiterAlignments(delimiterLine.text);
|
|
1491
|
+
if (!alignments) {
|
|
1492
|
+
return null;
|
|
1493
|
+
}
|
|
1494
|
+
let effectiveEndLineNumber = delimiterLineNumber;
|
|
1495
|
+
for (let lineNumber = delimiterLineNumber + 1; lineNumber <= endLine.number; lineNumber++) {
|
|
1496
|
+
const line = state.doc.line(lineNumber);
|
|
1497
|
+
if (!isTableRowLine(line.text)) {
|
|
1498
|
+
break;
|
|
1499
|
+
}
|
|
1500
|
+
effectiveEndLineNumber = lineNumber;
|
|
1501
|
+
}
|
|
1502
|
+
const cellsByRow = [];
|
|
1503
|
+
for (let lineNumber = startLine.number; lineNumber <= effectiveEndLineNumber; lineNumber++) {
|
|
1504
|
+
if (lineNumber === delimiterLineNumber) {
|
|
1505
|
+
continue;
|
|
1506
|
+
}
|
|
1507
|
+
const line = state.doc.line(lineNumber);
|
|
1508
|
+
const pipes = getPipePositions(line.text);
|
|
1509
|
+
if (pipes.length < 2) {
|
|
1510
|
+
return null;
|
|
1511
|
+
}
|
|
1512
|
+
const isHeader = lineNumber === startLine.number;
|
|
1513
|
+
const rowIndex = isHeader ? 0 : cellsByRow.length;
|
|
1514
|
+
const cells = [];
|
|
1515
|
+
for (let columnIndex = 0; columnIndex < pipes.length - 1; columnIndex++) {
|
|
1516
|
+
const from = line.from + pipes[columnIndex] + 1;
|
|
1517
|
+
const to = line.from + pipes[columnIndex + 1];
|
|
1518
|
+
const rawText = line.text.slice(pipes[columnIndex] + 1, pipes[columnIndex + 1]);
|
|
1519
|
+
const visible = getVisibleBounds(rawText);
|
|
1520
|
+
cells.push({
|
|
1521
|
+
rowKind: isHeader ? "header" : "body",
|
|
1522
|
+
rowIndex,
|
|
1523
|
+
columnIndex,
|
|
1524
|
+
from,
|
|
1525
|
+
to,
|
|
1526
|
+
contentFrom: from + visible.startOffset,
|
|
1527
|
+
contentTo: from + visible.endOffset,
|
|
1528
|
+
lineFrom: line.from,
|
|
1529
|
+
lineNumber,
|
|
1530
|
+
rawText
|
|
1271
1531
|
});
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
return wrapper;
|
|
1532
|
+
}
|
|
1533
|
+
cellsByRow.push(cells);
|
|
1275
1534
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1535
|
+
if (cellsByRow.length === 0) {
|
|
1536
|
+
return null;
|
|
1537
|
+
}
|
|
1538
|
+
return {
|
|
1539
|
+
from: startLine.from,
|
|
1540
|
+
to: state.doc.line(effectiveEndLineNumber).to,
|
|
1541
|
+
startLineNumber: startLine.number,
|
|
1542
|
+
delimiterLineNumber,
|
|
1543
|
+
endLineNumber: effectiveEndLineNumber,
|
|
1544
|
+
columnCount: cellsByRow[0].length,
|
|
1545
|
+
alignments: Array.from({ length: cellsByRow[0].length }, (_, index) => alignments[index] || "left"),
|
|
1546
|
+
cellsByRow,
|
|
1547
|
+
headerCells: cellsByRow[0],
|
|
1548
|
+
bodyCells: cellsByRow.slice(1)
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
function getTableInfoAtPosition(state, position) {
|
|
1552
|
+
let resolved = null;
|
|
1553
|
+
syntaxTree(state).iterate({
|
|
1554
|
+
enter: (node) => {
|
|
1555
|
+
if (resolved || node.name !== "Table") {
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
const info = readTableInfo(state, node.from, node.to);
|
|
1559
|
+
if (info && position >= info.from && position <= info.to) {
|
|
1560
|
+
resolved = info;
|
|
1561
|
+
}
|
|
1283
1562
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1563
|
+
});
|
|
1564
|
+
return resolved;
|
|
1565
|
+
}
|
|
1566
|
+
function findCellAtPosition(tableInfo, position) {
|
|
1567
|
+
for (const row of tableInfo.cellsByRow) {
|
|
1568
|
+
for (const cell of row) {
|
|
1569
|
+
if (position >= cell.from && position <= cell.to) {
|
|
1570
|
+
return cell;
|
|
1571
|
+
}
|
|
1289
1572
|
}
|
|
1290
1573
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1574
|
+
for (const row of tableInfo.cellsByRow) {
|
|
1575
|
+
for (const cell of row) {
|
|
1576
|
+
if (position >= cell.from - 1 && position <= cell.to + 1) {
|
|
1577
|
+
return cell;
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
let nearestCell = null;
|
|
1582
|
+
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
1583
|
+
for (const row of tableInfo.cellsByRow) {
|
|
1584
|
+
for (const cell of row) {
|
|
1585
|
+
const distance = Math.min(Math.abs(position - cell.from), Math.abs(position - cell.to));
|
|
1586
|
+
if (distance < nearestDistance) {
|
|
1587
|
+
nearestCell = cell;
|
|
1588
|
+
nearestDistance = distance;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
return nearestCell;
|
|
1593
|
+
}
|
|
1594
|
+
function clampCellPosition(cell, position) {
|
|
1595
|
+
const cellEnd = Math.max(cell.contentFrom, cell.contentTo);
|
|
1596
|
+
return Math.max(cell.contentFrom, Math.min(position, cellEnd));
|
|
1597
|
+
}
|
|
1598
|
+
function collectBreakRanges(tableInfo) {
|
|
1599
|
+
const ranges = [];
|
|
1600
|
+
for (const row of tableInfo.cellsByRow) {
|
|
1601
|
+
for (const cell of row) {
|
|
1602
|
+
let match;
|
|
1603
|
+
const regex = new RegExp(BREAK_TAG_REGEX);
|
|
1604
|
+
while ((match = regex.exec(cell.rawText)) !== null) {
|
|
1605
|
+
ranges.push({
|
|
1606
|
+
from: cell.from + match.index,
|
|
1607
|
+
to: cell.from + match.index + match[0].length
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1293
1611
|
}
|
|
1612
|
+
return ranges;
|
|
1613
|
+
}
|
|
1614
|
+
var lineDecorations = {
|
|
1615
|
+
header: Decoration.line({ class: "cm-draftly-table-row cm-draftly-table-header-row" }),
|
|
1616
|
+
delimiter: Decoration.line({ class: "cm-draftly-table-row cm-draftly-table-delimiter-row" }),
|
|
1617
|
+
body: Decoration.line({ class: "cm-draftly-table-row cm-draftly-table-body-row" }),
|
|
1618
|
+
even: Decoration.line({ class: "cm-draftly-table-row cm-draftly-table-body-row cm-draftly-table-row-even" }),
|
|
1619
|
+
last: Decoration.line({ class: "cm-draftly-table-row-last" })
|
|
1294
1620
|
};
|
|
1295
|
-
var
|
|
1296
|
-
"
|
|
1297
|
-
"
|
|
1298
|
-
"
|
|
1299
|
-
"
|
|
1300
|
-
"
|
|
1301
|
-
|
|
1302
|
-
|
|
1621
|
+
var cellDecorations = {
|
|
1622
|
+
"th-left": Decoration.mark({ class: "cm-draftly-table-cell cm-draftly-table-th" }),
|
|
1623
|
+
"th-center": Decoration.mark({ class: "cm-draftly-table-cell cm-draftly-table-th cm-draftly-table-cell-center" }),
|
|
1624
|
+
"th-right": Decoration.mark({ class: "cm-draftly-table-cell cm-draftly-table-th cm-draftly-table-cell-right" }),
|
|
1625
|
+
"th-left-last": Decoration.mark({ class: "cm-draftly-table-cell cm-draftly-table-th cm-draftly-table-cell-last" }),
|
|
1626
|
+
"th-center-last": Decoration.mark({
|
|
1627
|
+
class: "cm-draftly-table-cell cm-draftly-table-th cm-draftly-table-cell-center cm-draftly-table-cell-last"
|
|
1628
|
+
}),
|
|
1629
|
+
"th-right-last": Decoration.mark({
|
|
1630
|
+
class: "cm-draftly-table-cell cm-draftly-table-th cm-draftly-table-cell-right cm-draftly-table-cell-last"
|
|
1631
|
+
}),
|
|
1632
|
+
"td-left": Decoration.mark({ class: "cm-draftly-table-cell" }),
|
|
1633
|
+
"td-center": Decoration.mark({ class: "cm-draftly-table-cell cm-draftly-table-cell-center" }),
|
|
1634
|
+
"td-right": Decoration.mark({ class: "cm-draftly-table-cell cm-draftly-table-cell-right" }),
|
|
1635
|
+
"td-left-last": Decoration.mark({ class: "cm-draftly-table-cell cm-draftly-table-cell-last" }),
|
|
1636
|
+
"td-center-last": Decoration.mark({
|
|
1637
|
+
class: "cm-draftly-table-cell cm-draftly-table-cell-center cm-draftly-table-cell-last"
|
|
1638
|
+
}),
|
|
1639
|
+
"td-right-last": Decoration.mark({
|
|
1640
|
+
class: "cm-draftly-table-cell cm-draftly-table-cell-right cm-draftly-table-cell-last"
|
|
1641
|
+
})
|
|
1303
1642
|
};
|
|
1643
|
+
function getCellDecoration(isHeader, alignment, isLastCell) {
|
|
1644
|
+
const key = `${isHeader ? "th" : "td"}-${alignment}${isLastCell ? "-last" : ""}`;
|
|
1645
|
+
return cellDecorations[key];
|
|
1646
|
+
}
|
|
1304
1647
|
var TablePlugin = class extends DecorationPlugin {
|
|
1305
1648
|
name = "table";
|
|
1306
|
-
version = "
|
|
1649
|
+
version = "2.0.0";
|
|
1307
1650
|
decorationPriority = 20;
|
|
1308
1651
|
requiredNodes = ["Table", "TableHeader", "TableDelimiter", "TableRow", "TableCell"];
|
|
1309
|
-
/** Configuration stored from onRegister */
|
|
1310
1652
|
draftlyConfig;
|
|
1653
|
+
pendingNormalizationView = null;
|
|
1654
|
+
pendingPaddingView = null;
|
|
1655
|
+
pendingSelectionRepairView = null;
|
|
1656
|
+
/** Stores the editor config for preview rendering and shared behavior. */
|
|
1311
1657
|
onRegister(context) {
|
|
1312
1658
|
super.onRegister(context);
|
|
1313
1659
|
this.draftlyConfig = context.config;
|
|
1314
1660
|
}
|
|
1661
|
+
/** Exposes the plugin theme used for editor and preview styling. */
|
|
1315
1662
|
get theme() {
|
|
1316
1663
|
return theme6;
|
|
1317
1664
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1665
|
+
/** Enables GFM table parsing for the editor and preview renderer. */
|
|
1666
|
+
getMarkdownConfig() {
|
|
1667
|
+
return Table;
|
|
1668
|
+
}
|
|
1669
|
+
/** Registers block wrappers and atomic ranges for the table UI. */
|
|
1670
|
+
getExtensions() {
|
|
1671
|
+
return [
|
|
1672
|
+
Prec.highest(keymap.of(this.buildTableKeymap())),
|
|
1673
|
+
EditorView.blockWrappers.of((view) => this.computeBlockWrappers(view)),
|
|
1674
|
+
EditorView.atomicRanges.of((view) => this.computeAtomicRanges(view)),
|
|
1675
|
+
EditorView.domEventHandlers({
|
|
1676
|
+
keydown: (event, view) => this.handleDomKeydown(view, event)
|
|
1677
|
+
})
|
|
1678
|
+
];
|
|
1679
|
+
}
|
|
1680
|
+
/** Provides the table-specific keyboard shortcuts and navigation. */
|
|
1321
1681
|
getKeymap() {
|
|
1682
|
+
return [];
|
|
1683
|
+
}
|
|
1684
|
+
/** Builds the high-priority key bindings used inside tables. */
|
|
1685
|
+
buildTableKeymap() {
|
|
1322
1686
|
return [
|
|
1323
|
-
{
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
},
|
|
1328
|
-
{
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
},
|
|
1333
|
-
{
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
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
|
-
}
|
|
1687
|
+
{ key: "Mod-Shift-t", run: (view) => this.insertTable(view), preventDefault: true },
|
|
1688
|
+
{ key: "Mod-Alt-ArrowDown", run: (view) => this.addRow(view), preventDefault: true },
|
|
1689
|
+
{ key: "Mod-Alt-ArrowRight", run: (view) => this.addColumn(view), preventDefault: true },
|
|
1690
|
+
{ key: "Mod-Alt-Backspace", run: (view) => this.removeRow(view), preventDefault: true },
|
|
1691
|
+
{ key: "Mod-Alt-Delete", run: (view) => this.removeColumn(view), preventDefault: true },
|
|
1692
|
+
{ key: "Tab", run: (view) => this.handleTab(view, false) },
|
|
1693
|
+
{ key: "Shift-Tab", run: (view) => this.handleTab(view, true) },
|
|
1694
|
+
{ key: "ArrowLeft", run: (view) => this.handleArrowHorizontal(view, false) },
|
|
1695
|
+
{ key: "ArrowRight", run: (view) => this.handleArrowHorizontal(view, true) },
|
|
1696
|
+
{ key: "ArrowUp", run: (view) => this.handleArrowVertical(view, false) },
|
|
1697
|
+
{ key: "ArrowDown", run: (view) => this.handleArrowVertical(view, true) },
|
|
1698
|
+
{ key: "Enter", run: (view) => this.handleEnter(view) },
|
|
1699
|
+
{ key: "Shift-Enter", run: (view) => this.insertBreakTag(view), preventDefault: true },
|
|
1700
|
+
{ key: "Backspace", run: (view) => this.handleBreakDeletion(view, false) },
|
|
1701
|
+
{ key: "Delete", run: (view) => this.handleBreakDeletion(view, true) }
|
|
1350
1702
|
];
|
|
1351
1703
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1704
|
+
/** Schedules an initial normalization pass once the view is ready. */
|
|
1705
|
+
onViewReady(view) {
|
|
1706
|
+
this.scheduleNormalization(view);
|
|
1707
|
+
}
|
|
1708
|
+
/** Re-schedules normalization after user-driven document changes. */
|
|
1709
|
+
onViewUpdate(update) {
|
|
1710
|
+
if (update.docChanged && !update.transactions.some((transaction) => transaction.annotation(normalizeAnnotation))) {
|
|
1711
|
+
this.schedulePadding(update.view);
|
|
1712
|
+
}
|
|
1713
|
+
if (update.selectionSet && !update.transactions.some((transaction) => transaction.annotation(repairSelectionAnnotation))) {
|
|
1714
|
+
this.scheduleSelectionRepair(update.view);
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
/** Intercepts table-specific DOM key handling before browser defaults run. */
|
|
1718
|
+
handleDomKeydown(view, event) {
|
|
1719
|
+
if (event.defaultPrevented || event.isComposing || event.altKey || event.metaKey || event.ctrlKey) {
|
|
1720
|
+
return false;
|
|
1721
|
+
}
|
|
1722
|
+
let handled = false;
|
|
1723
|
+
if (event.key === "Tab") {
|
|
1724
|
+
handled = this.handleTab(view, event.shiftKey);
|
|
1725
|
+
} else if (event.key === "Enter" && event.shiftKey) {
|
|
1726
|
+
handled = this.insertBreakTag(view);
|
|
1727
|
+
} else if (event.key === "Enter") {
|
|
1728
|
+
handled = this.handleEnter(view);
|
|
1729
|
+
} else if (event.key === "ArrowLeft") {
|
|
1730
|
+
handled = this.handleArrowHorizontal(view, false);
|
|
1731
|
+
} else if (event.key === "ArrowRight") {
|
|
1732
|
+
handled = this.handleArrowHorizontal(view, true);
|
|
1733
|
+
} else if (event.key === "ArrowUp") {
|
|
1734
|
+
handled = this.handleArrowVertical(view, false);
|
|
1735
|
+
} else if (event.key === "ArrowDown") {
|
|
1736
|
+
handled = this.handleArrowVertical(view, true);
|
|
1737
|
+
} else if (event.key === "Backspace") {
|
|
1738
|
+
handled = this.handleBreakDeletion(view, false);
|
|
1739
|
+
} else if (event.key === "Delete") {
|
|
1740
|
+
handled = this.handleBreakDeletion(view, true);
|
|
1741
|
+
}
|
|
1742
|
+
if (handled) {
|
|
1743
|
+
event.preventDefault();
|
|
1744
|
+
event.stopPropagation();
|
|
1745
|
+
}
|
|
1746
|
+
return handled;
|
|
1747
|
+
}
|
|
1748
|
+
/** Builds the visual table decorations for every parsed table block. */
|
|
1355
1749
|
buildDecorations(ctx) {
|
|
1356
1750
|
const { view, decorations } = ctx;
|
|
1357
|
-
|
|
1358
|
-
tree.iterate({
|
|
1751
|
+
syntaxTree(view.state).iterate({
|
|
1359
1752
|
enter: (node) => {
|
|
1360
|
-
if (node.name !== "Table")
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
const
|
|
1364
|
-
|
|
1365
|
-
|
|
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
|
-
);
|
|
1753
|
+
if (node.name !== "Table") {
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
const tableInfo = readTableInfo(view.state, node.from, node.to);
|
|
1757
|
+
if (tableInfo) {
|
|
1758
|
+
this.decorateTable(view, decorations, tableInfo);
|
|
1393
1759
|
}
|
|
1394
1760
|
}
|
|
1395
1761
|
});
|
|
1396
1762
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1763
|
+
/** Renders the full table node to semantic preview HTML. */
|
|
1764
|
+
async renderToHTML(node, _children, ctx) {
|
|
1765
|
+
if (node.name === "Table") {
|
|
1766
|
+
const content = ctx.sliceDoc(node.from, node.to);
|
|
1767
|
+
const { tableMarkdown, trailingMarkdown } = splitTableAndTrailingMarkdown(content);
|
|
1768
|
+
const parsed = parseTableMarkdown(tableMarkdown);
|
|
1769
|
+
if (!parsed) {
|
|
1770
|
+
return null;
|
|
1771
|
+
}
|
|
1772
|
+
const tableHtml = await renderTableToHtml(parsed, this.draftlyConfig);
|
|
1773
|
+
if (!trailingMarkdown.trim()) {
|
|
1774
|
+
return tableHtml;
|
|
1775
|
+
}
|
|
1776
|
+
return tableHtml + await createPreviewRenderer(trailingMarkdown, this.draftlyConfig).render();
|
|
1777
|
+
}
|
|
1778
|
+
if (TABLE_SUB_NODE_NAMES.has(node.name)) {
|
|
1779
|
+
return "";
|
|
1780
|
+
}
|
|
1781
|
+
return null;
|
|
1782
|
+
}
|
|
1783
|
+
/** Computes the block wrapper ranges used to group table lines. */
|
|
1784
|
+
computeBlockWrappers(view) {
|
|
1785
|
+
const wrappers = [];
|
|
1786
|
+
syntaxTree(view.state).iterate({
|
|
1787
|
+
enter: (node) => {
|
|
1788
|
+
if (node.name !== "Table") {
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
const tableInfo = readTableInfo(view.state, node.from, node.to);
|
|
1792
|
+
if (tableInfo) {
|
|
1793
|
+
wrappers.push(tableBlockWrapper.range(tableInfo.from, tableInfo.to));
|
|
1794
|
+
}
|
|
1423
1795
|
}
|
|
1424
1796
|
});
|
|
1425
|
-
return true;
|
|
1797
|
+
return BlockWrapper.set(wrappers, true);
|
|
1426
1798
|
}
|
|
1427
|
-
/**
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1799
|
+
/** Computes atomic ranges for delimiters and inline break tags. */
|
|
1800
|
+
computeAtomicRanges(view) {
|
|
1801
|
+
const ranges = [];
|
|
1802
|
+
syntaxTree(view.state).iterate({
|
|
1803
|
+
enter: (node) => {
|
|
1804
|
+
if (node.name !== "Table") {
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
const tableInfo = readTableInfo(view.state, node.from, node.to);
|
|
1808
|
+
if (!tableInfo) {
|
|
1809
|
+
return;
|
|
1810
|
+
}
|
|
1811
|
+
for (let lineNumber = tableInfo.startLineNumber; lineNumber <= tableInfo.endLineNumber; lineNumber++) {
|
|
1812
|
+
const line = view.state.doc.line(lineNumber);
|
|
1813
|
+
if (lineNumber === tableInfo.delimiterLineNumber) {
|
|
1814
|
+
ranges.push(delimiterReplace.range(line.from, line.to));
|
|
1815
|
+
continue;
|
|
1816
|
+
}
|
|
1817
|
+
const pipes = getPipePositions(line.text);
|
|
1818
|
+
for (const pipe of pipes) {
|
|
1819
|
+
ranges.push(pipeReplace.range(line.from + pipe, line.from + pipe + 1));
|
|
1820
|
+
}
|
|
1821
|
+
for (let columnIndex = 0; columnIndex < pipes.length - 1; columnIndex++) {
|
|
1822
|
+
const rawFrom = pipes[columnIndex] + 1;
|
|
1823
|
+
const rawTo = pipes[columnIndex + 1];
|
|
1824
|
+
const rawText = line.text.slice(rawFrom, rawTo);
|
|
1825
|
+
const visible = getVisibleBounds(rawText);
|
|
1826
|
+
if (visible.startOffset > 0) {
|
|
1827
|
+
ranges.push(pipeReplace.range(line.from + rawFrom, line.from + rawFrom + visible.startOffset));
|
|
1828
|
+
}
|
|
1829
|
+
if (visible.endOffset < rawText.length) {
|
|
1830
|
+
ranges.push(pipeReplace.range(line.from + rawFrom + visible.endOffset, line.from + rawTo));
|
|
1831
|
+
}
|
|
1832
|
+
let match;
|
|
1833
|
+
const regex = new RegExp(BREAK_TAG_REGEX);
|
|
1834
|
+
while ((match = regex.exec(rawText)) !== null) {
|
|
1835
|
+
ranges.push(
|
|
1836
|
+
Decoration.replace({ widget: new TableBreakWidget() }).range(
|
|
1837
|
+
line.from + rawFrom + match.index,
|
|
1838
|
+
line.from + rawFrom + match.index + match[0].length
|
|
1839
|
+
)
|
|
1840
|
+
);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1448
1844
|
}
|
|
1449
1845
|
});
|
|
1450
|
-
return true;
|
|
1846
|
+
return RangeSet.of(ranges, true);
|
|
1451
1847
|
}
|
|
1452
|
-
/**
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
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, " -------- ");
|
|
1848
|
+
/** Applies row, cell, and control decorations for a single table. */
|
|
1849
|
+
decorateTable(view, decorations, tableInfo) {
|
|
1850
|
+
for (let lineNumber = tableInfo.startLineNumber; lineNumber <= tableInfo.endLineNumber; lineNumber++) {
|
|
1851
|
+
const line = view.state.doc.line(lineNumber);
|
|
1852
|
+
const isHeader = lineNumber === tableInfo.startLineNumber;
|
|
1853
|
+
const isDelimiter = lineNumber === tableInfo.delimiterLineNumber;
|
|
1854
|
+
const isLastBody = !isHeader && !isDelimiter && lineNumber === tableInfo.endLineNumber;
|
|
1855
|
+
const bodyIndex = isHeader || isDelimiter ? -1 : lineNumber - tableInfo.delimiterLineNumber - 1;
|
|
1856
|
+
if (isHeader) {
|
|
1857
|
+
decorations.push(lineDecorations.header.range(line.from));
|
|
1858
|
+
} else if (isDelimiter) {
|
|
1859
|
+
decorations.push(lineDecorations.delimiter.range(line.from));
|
|
1860
|
+
} else if (bodyIndex % 2 === 1) {
|
|
1861
|
+
decorations.push(lineDecorations.even.range(line.from));
|
|
1475
1862
|
} else {
|
|
1476
|
-
|
|
1863
|
+
decorations.push(lineDecorations.body.range(line.from));
|
|
1477
1864
|
}
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
view.dispatch({
|
|
1481
|
-
changes: {
|
|
1482
|
-
from: tableInfo.from,
|
|
1483
|
-
to: tableInfo.to,
|
|
1484
|
-
insert: newLines.join("\n")
|
|
1865
|
+
if (isLastBody) {
|
|
1866
|
+
decorations.push(lineDecorations.last.range(line.from));
|
|
1485
1867
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1868
|
+
if (isDelimiter) {
|
|
1869
|
+
decorations.push(delimiterReplace.range(line.from, line.to));
|
|
1870
|
+
continue;
|
|
1871
|
+
}
|
|
1872
|
+
this.decorateLine(decorations, line.from, line.text, tableInfo.alignments, isHeader);
|
|
1873
|
+
}
|
|
1874
|
+
decorations.push(
|
|
1875
|
+
Decoration.widget({
|
|
1876
|
+
widget: new TableControlsWidget(
|
|
1877
|
+
(view2) => {
|
|
1878
|
+
const liveTable = getTableInfoAtPosition(view2.state, tableInfo.from);
|
|
1879
|
+
if (liveTable) {
|
|
1880
|
+
this.appendRow(view2, liveTable, liveTable.columnCount - 1);
|
|
1881
|
+
}
|
|
1882
|
+
},
|
|
1883
|
+
(view2) => {
|
|
1884
|
+
const liveTable = getTableInfoAtPosition(view2.state, tableInfo.from);
|
|
1885
|
+
if (liveTable) {
|
|
1886
|
+
this.appendColumn(view2, liveTable);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
),
|
|
1890
|
+
side: 1
|
|
1891
|
+
}).range(tableInfo.to)
|
|
1892
|
+
);
|
|
1488
1893
|
}
|
|
1489
|
-
/**
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
const
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1894
|
+
/** Applies the visual cell decorations for a single table row line. */
|
|
1895
|
+
decorateLine(decorations, lineFrom, lineText, alignments, isHeader) {
|
|
1896
|
+
const pipes = getPipePositions(lineText);
|
|
1897
|
+
if (pipes.length < 2) {
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
for (const pipe of pipes) {
|
|
1901
|
+
decorations.push(pipeReplace.range(lineFrom + pipe, lineFrom + pipe + 1));
|
|
1902
|
+
}
|
|
1903
|
+
for (let columnIndex = 0; columnIndex < pipes.length - 1; columnIndex++) {
|
|
1904
|
+
const rawFrom = pipes[columnIndex] + 1;
|
|
1905
|
+
const rawTo = pipes[columnIndex + 1];
|
|
1906
|
+
const rawText = lineText.slice(rawFrom, rawTo);
|
|
1907
|
+
const visible = getVisibleBounds(rawText);
|
|
1908
|
+
const absoluteFrom = lineFrom + rawFrom;
|
|
1909
|
+
const absoluteTo = lineFrom + rawTo;
|
|
1910
|
+
if (visible.startOffset > 0) {
|
|
1911
|
+
decorations.push(pipeReplace.range(absoluteFrom, absoluteFrom + visible.startOffset));
|
|
1912
|
+
}
|
|
1913
|
+
if (visible.endOffset < rawText.length) {
|
|
1914
|
+
decorations.push(pipeReplace.range(absoluteFrom + visible.endOffset, absoluteTo));
|
|
1915
|
+
}
|
|
1916
|
+
decorations.push(
|
|
1917
|
+
getCellDecoration(isHeader, alignments[columnIndex] || "left", columnIndex === pipes.length - 2).range(
|
|
1918
|
+
absoluteFrom,
|
|
1919
|
+
absoluteTo
|
|
1920
|
+
)
|
|
1921
|
+
);
|
|
1922
|
+
let match;
|
|
1923
|
+
const regex = new RegExp(BREAK_TAG_REGEX);
|
|
1924
|
+
while ((match = regex.exec(rawText)) !== null) {
|
|
1925
|
+
decorations.push(
|
|
1926
|
+
Decoration.replace({ widget: new TableBreakWidget() }).range(
|
|
1927
|
+
absoluteFrom + match.index,
|
|
1928
|
+
absoluteFrom + match.index + match[0].length
|
|
1929
|
+
)
|
|
1930
|
+
);
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
/** Normalizes every parsed table block back into canonical markdown. */
|
|
1935
|
+
normalizeTables(view) {
|
|
1936
|
+
const changes = [];
|
|
1937
|
+
syntaxTree(view.state).iterate({
|
|
1938
|
+
enter: (node) => {
|
|
1939
|
+
if (node.name !== "Table") {
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
const content = view.state.sliceDoc(node.from, node.to);
|
|
1943
|
+
const { tableMarkdown } = splitTableAndTrailingMarkdown(content);
|
|
1944
|
+
const parsed = parseTableMarkdown(tableMarkdown);
|
|
1945
|
+
if (!parsed) {
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
const formatted = formatTableMarkdown(parsed);
|
|
1949
|
+
const change = createTableInsert(view.state, node.from, node.from + tableMarkdown.length, formatted);
|
|
1950
|
+
if (change.insert !== tableMarkdown || change.from !== node.from || change.to !== node.from + tableMarkdown.length) {
|
|
1951
|
+
changes.push({
|
|
1952
|
+
from: change.from,
|
|
1953
|
+
to: change.to,
|
|
1954
|
+
insert: change.insert
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
});
|
|
1959
|
+
if (changes.length > 0) {
|
|
1514
1960
|
view.dispatch({
|
|
1515
|
-
changes:
|
|
1516
|
-
|
|
1517
|
-
|
|
1961
|
+
changes: changes.sort((left, right) => right.from - left.from),
|
|
1962
|
+
annotations: normalizeAnnotation.of(true)
|
|
1963
|
+
});
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
/** Defers table normalization until the current update cycle is finished. */
|
|
1967
|
+
scheduleNormalization(view) {
|
|
1968
|
+
if (this.pendingNormalizationView === view) {
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
this.pendingNormalizationView = view;
|
|
1972
|
+
queueMicrotask(() => {
|
|
1973
|
+
if (this.pendingNormalizationView !== view) {
|
|
1974
|
+
return;
|
|
1975
|
+
}
|
|
1976
|
+
this.pendingNormalizationView = null;
|
|
1977
|
+
this.normalizeTables(view);
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
/** Adds missing spacer lines above and below tables after edits. */
|
|
1981
|
+
ensureTablePadding(view) {
|
|
1982
|
+
const changes = [];
|
|
1983
|
+
syntaxTree(view.state).iterate({
|
|
1984
|
+
enter: (node) => {
|
|
1985
|
+
if (node.name !== "Table") {
|
|
1986
|
+
return;
|
|
1987
|
+
}
|
|
1988
|
+
const tableInfo = readTableInfo(view.state, node.from, node.to);
|
|
1989
|
+
if (!tableInfo) {
|
|
1990
|
+
return;
|
|
1991
|
+
}
|
|
1992
|
+
const startLine = view.state.doc.lineAt(tableInfo.from);
|
|
1993
|
+
if (startLine.number === 1) {
|
|
1994
|
+
changes.push({ from: startLine.from, to: startLine.from, insert: "\n" });
|
|
1995
|
+
} else {
|
|
1996
|
+
const previousLine = view.state.doc.line(startLine.number - 1);
|
|
1997
|
+
if (previousLine.text.trim() !== "") {
|
|
1998
|
+
changes.push({ from: startLine.from, to: startLine.from, insert: "\n" });
|
|
1999
|
+
}
|
|
1518
2000
|
}
|
|
2001
|
+
const endLine = view.state.doc.lineAt(tableInfo.to);
|
|
2002
|
+
if (endLine.number === view.state.doc.lines) {
|
|
2003
|
+
changes.push({ from: endLine.to, to: endLine.to, insert: "\n" });
|
|
2004
|
+
} else {
|
|
2005
|
+
const nextLine = view.state.doc.line(endLine.number + 1);
|
|
2006
|
+
if (nextLine.text.trim() !== "") {
|
|
2007
|
+
changes.push({ from: endLine.to, to: endLine.to, insert: "\n" });
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
});
|
|
2012
|
+
if (changes.length > 0) {
|
|
2013
|
+
view.dispatch({
|
|
2014
|
+
changes: changes.sort((left, right) => right.from - left.from),
|
|
2015
|
+
annotations: normalizeAnnotation.of(true)
|
|
1519
2016
|
});
|
|
1520
|
-
return true;
|
|
1521
2017
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
2018
|
+
}
|
|
2019
|
+
/** Schedules a padding-only pass after the current update cycle finishes. */
|
|
2020
|
+
schedulePadding(view) {
|
|
2021
|
+
if (this.pendingPaddingView === view) {
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
2024
|
+
this.pendingPaddingView = view;
|
|
2025
|
+
queueMicrotask(() => {
|
|
2026
|
+
if (this.pendingPaddingView !== view) {
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
this.pendingPaddingView = null;
|
|
2030
|
+
this.ensureTablePadding(view);
|
|
2031
|
+
});
|
|
2032
|
+
}
|
|
2033
|
+
/** Repairs carets that land in hidden table markup instead of editable cell content. */
|
|
2034
|
+
ensureTableSelection(view) {
|
|
2035
|
+
const selection = view.state.selection.main;
|
|
2036
|
+
if (!selection.empty) {
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
const tableInfo = getTableInfoAtPosition(view.state, selection.head);
|
|
2040
|
+
if (!tableInfo) {
|
|
2041
|
+
return;
|
|
2042
|
+
}
|
|
2043
|
+
const cell = findCellAtPosition(tableInfo, selection.head);
|
|
2044
|
+
if (!cell) {
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
const anchor = clampCellPosition(cell, selection.head);
|
|
2048
|
+
if (anchor === selection.head) {
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
1526
2051
|
view.dispatch({
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
2052
|
+
selection: { anchor },
|
|
2053
|
+
annotations: repairSelectionAnnotation.of(true),
|
|
2054
|
+
scrollIntoView: true
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
/** Schedules table selection repair after the current update finishes. */
|
|
2058
|
+
scheduleSelectionRepair(view) {
|
|
2059
|
+
if (this.pendingSelectionRepairView === view) {
|
|
2060
|
+
return;
|
|
2061
|
+
}
|
|
2062
|
+
this.pendingSelectionRepairView = view;
|
|
2063
|
+
queueMicrotask(() => {
|
|
2064
|
+
if (this.pendingSelectionRepairView !== view) {
|
|
2065
|
+
return;
|
|
1534
2066
|
}
|
|
2067
|
+
this.pendingSelectionRepairView = null;
|
|
2068
|
+
this.ensureTableSelection(view);
|
|
2069
|
+
});
|
|
2070
|
+
}
|
|
2071
|
+
/** Rewrites a table block and restores the caret to a target cell position. */
|
|
2072
|
+
replaceTable(view, tableInfo, parsed, targetRowIndex, targetColumnIndex, offset = 0) {
|
|
2073
|
+
const formatted = formatTableMarkdown(parsed);
|
|
2074
|
+
const change = createTableInsert(view.state, tableInfo.from, tableInfo.to, formatted);
|
|
2075
|
+
const selection = change.from + change.prefixLength + getCellAnchorInFormattedTable(
|
|
2076
|
+
formatted,
|
|
2077
|
+
Math.max(0, targetRowIndex),
|
|
2078
|
+
Math.max(0, Math.min(targetColumnIndex, Math.max(parsed.headers.length - 1, 0))),
|
|
2079
|
+
Math.max(0, offset)
|
|
2080
|
+
);
|
|
2081
|
+
view.dispatch({
|
|
2082
|
+
changes: { from: change.from, to: change.to, insert: change.insert },
|
|
2083
|
+
selection: { anchor: selection }
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
/** Inserts an empty body row below the given logical row index. */
|
|
2087
|
+
insertRowBelow(view, tableInfo, afterRowIndex, targetColumn) {
|
|
2088
|
+
const parsed = normalizeParsedTable(buildTableFromInfo(tableInfo));
|
|
2089
|
+
const insertBodyIndex = Math.max(0, Math.min(afterRowIndex, parsed.rows.length));
|
|
2090
|
+
parsed.rows.splice(insertBodyIndex, 0, buildEmptyRow(tableInfo.columnCount));
|
|
2091
|
+
this.replaceTable(view, tableInfo, parsed, insertBodyIndex + 1, targetColumn);
|
|
2092
|
+
}
|
|
2093
|
+
/** Inserts a starter table near the current cursor line. */
|
|
2094
|
+
insertTable(view) {
|
|
2095
|
+
const { state } = view;
|
|
2096
|
+
const cursor = state.selection.main.head;
|
|
2097
|
+
const line = state.doc.lineAt(cursor);
|
|
2098
|
+
const insertAt = line.text.trim() ? line.to : line.from;
|
|
2099
|
+
const formatted = formatTableMarkdown(TABLE_TEMPLATE);
|
|
2100
|
+
const change = createTableInsert(state, insertAt, insertAt, formatted);
|
|
2101
|
+
const selection = change.from + change.prefixLength + getCellAnchorInFormattedTable(formatted, 0, 0);
|
|
2102
|
+
view.dispatch({
|
|
2103
|
+
changes: { from: change.from, to: change.to, insert: change.insert },
|
|
2104
|
+
selection: { anchor: selection }
|
|
1535
2105
|
});
|
|
1536
2106
|
return true;
|
|
1537
2107
|
}
|
|
1538
|
-
/**
|
|
1539
|
-
|
|
1540
|
-
|
|
2108
|
+
/** Adds a new empty body row to the active table. */
|
|
2109
|
+
addRow(view) {
|
|
2110
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
2111
|
+
if (!tableInfo) {
|
|
2112
|
+
return false;
|
|
2113
|
+
}
|
|
2114
|
+
const cell = this.getCurrentCell(view, tableInfo);
|
|
2115
|
+
this.appendRow(view, tableInfo, cell?.columnIndex || 0);
|
|
2116
|
+
return true;
|
|
2117
|
+
}
|
|
2118
|
+
/** Appends a row and keeps the caret in the requested column. */
|
|
2119
|
+
appendRow(view, tableInfo, targetColumn) {
|
|
2120
|
+
this.insertRowBelow(view, tableInfo, tableInfo.bodyCells.length, targetColumn);
|
|
2121
|
+
}
|
|
2122
|
+
/** Inserts a new column after the current column. */
|
|
2123
|
+
addColumn(view) {
|
|
2124
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
2125
|
+
if (!tableInfo) {
|
|
2126
|
+
return false;
|
|
2127
|
+
}
|
|
2128
|
+
const cell = this.getCurrentCell(view, tableInfo);
|
|
2129
|
+
const insertAfter = cell?.columnIndex ?? tableInfo.columnCount - 1;
|
|
2130
|
+
const parsed = normalizeParsedTable(buildTableFromInfo(tableInfo));
|
|
2131
|
+
parsed.headers.splice(insertAfter + 1, 0, "");
|
|
2132
|
+
parsed.alignments.splice(insertAfter + 1, 0, "left");
|
|
2133
|
+
for (const row of parsed.rows) {
|
|
2134
|
+
row.splice(insertAfter + 1, 0, "");
|
|
2135
|
+
}
|
|
2136
|
+
this.replaceTable(view, tableInfo, parsed, cell?.rowIndex || 0, insertAfter + 1);
|
|
2137
|
+
return true;
|
|
2138
|
+
}
|
|
2139
|
+
/** Appends a new column at the far right of the table. */
|
|
2140
|
+
appendColumn(view, tableInfo) {
|
|
2141
|
+
const parsed = normalizeParsedTable(buildTableFromInfo(tableInfo));
|
|
2142
|
+
parsed.headers.push("");
|
|
2143
|
+
parsed.alignments.push("left");
|
|
2144
|
+
for (const row of parsed.rows) {
|
|
2145
|
+
row.push("");
|
|
2146
|
+
}
|
|
2147
|
+
this.replaceTable(view, tableInfo, parsed, 0, parsed.headers.length - 1);
|
|
2148
|
+
}
|
|
2149
|
+
/** Removes the current body row or clears the last remaining row. */
|
|
2150
|
+
removeRow(view) {
|
|
2151
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
2152
|
+
if (!tableInfo) {
|
|
2153
|
+
return false;
|
|
2154
|
+
}
|
|
2155
|
+
const cell = this.getCurrentCell(view, tableInfo);
|
|
2156
|
+
if (!cell || cell.rowKind !== "body") {
|
|
2157
|
+
return false;
|
|
2158
|
+
}
|
|
2159
|
+
const parsed = normalizeParsedTable(buildTableFromInfo(tableInfo));
|
|
2160
|
+
const bodyIndex = cell.rowIndex - 1;
|
|
2161
|
+
if (bodyIndex < 0 || bodyIndex >= parsed.rows.length) {
|
|
2162
|
+
return false;
|
|
2163
|
+
}
|
|
2164
|
+
if (parsed.rows.length === 1) {
|
|
2165
|
+
parsed.rows[0] = buildEmptyRow(tableInfo.columnCount);
|
|
2166
|
+
} else {
|
|
2167
|
+
parsed.rows.splice(bodyIndex, 1);
|
|
2168
|
+
}
|
|
2169
|
+
const nextRowIndex = Math.max(1, Math.min(cell.rowIndex, parsed.rows.length));
|
|
2170
|
+
this.replaceTable(view, tableInfo, parsed, nextRowIndex, Math.min(cell.columnIndex, tableInfo.columnCount - 1));
|
|
2171
|
+
return true;
|
|
2172
|
+
}
|
|
2173
|
+
/** Removes the current column when the table has more than one column. */
|
|
2174
|
+
removeColumn(view) {
|
|
2175
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
2176
|
+
if (!tableInfo || tableInfo.columnCount <= 1) {
|
|
2177
|
+
return false;
|
|
2178
|
+
}
|
|
2179
|
+
const cell = this.getCurrentCell(view, tableInfo);
|
|
2180
|
+
const removeAt = cell?.columnIndex ?? tableInfo.columnCount - 1;
|
|
2181
|
+
const parsed = normalizeParsedTable(buildTableFromInfo(tableInfo));
|
|
2182
|
+
parsed.headers.splice(removeAt, 1);
|
|
2183
|
+
parsed.alignments.splice(removeAt, 1);
|
|
2184
|
+
for (const row of parsed.rows) {
|
|
2185
|
+
row.splice(removeAt, 1);
|
|
2186
|
+
}
|
|
2187
|
+
this.replaceTable(view, tableInfo, parsed, cell?.rowIndex || 0, Math.min(removeAt, parsed.headers.length - 1));
|
|
2188
|
+
return true;
|
|
2189
|
+
}
|
|
2190
|
+
/** Moves to the next or previous logical cell with Tab navigation. */
|
|
1541
2191
|
handleTab(view, backwards) {
|
|
1542
2192
|
const tableInfo = this.getTableAtCursor(view);
|
|
1543
|
-
if (!tableInfo)
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
const
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
2193
|
+
if (!tableInfo) {
|
|
2194
|
+
return false;
|
|
2195
|
+
}
|
|
2196
|
+
const cell = this.getCurrentCell(view, tableInfo);
|
|
2197
|
+
if (!cell) {
|
|
2198
|
+
return false;
|
|
2199
|
+
}
|
|
2200
|
+
const cells = tableInfo.cellsByRow.flat();
|
|
2201
|
+
const currentIndex = cells.findIndex((candidate) => candidate.from === cell.from && candidate.to === cell.to);
|
|
2202
|
+
if (currentIndex < 0) {
|
|
2203
|
+
return false;
|
|
2204
|
+
}
|
|
2205
|
+
const nextIndex = backwards ? currentIndex - 1 : currentIndex + 1;
|
|
2206
|
+
if (nextIndex < 0) {
|
|
2207
|
+
return true;
|
|
2208
|
+
}
|
|
2209
|
+
if (nextIndex >= cells.length) {
|
|
2210
|
+
this.appendRow(view, tableInfo, 0);
|
|
2211
|
+
return true;
|
|
2212
|
+
}
|
|
2213
|
+
this.moveSelectionToCell(view, cells[nextIndex]);
|
|
2214
|
+
return true;
|
|
2215
|
+
}
|
|
2216
|
+
/** Moves horizontally between adjacent cells when the caret hits an edge. */
|
|
2217
|
+
handleArrowHorizontal(view, forward) {
|
|
2218
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
2219
|
+
if (!tableInfo) {
|
|
2220
|
+
return false;
|
|
2221
|
+
}
|
|
2222
|
+
const cell = this.getCurrentCell(view, tableInfo);
|
|
2223
|
+
if (!cell) {
|
|
2224
|
+
return false;
|
|
2225
|
+
}
|
|
2226
|
+
const cursor = view.state.selection.main.head;
|
|
2227
|
+
const rightEdge = Math.max(cell.contentFrom, cell.contentTo);
|
|
2228
|
+
if (forward && cursor < rightEdge) {
|
|
2229
|
+
return false;
|
|
2230
|
+
}
|
|
2231
|
+
if (!forward && cursor > cell.contentFrom) {
|
|
2232
|
+
return false;
|
|
2233
|
+
}
|
|
2234
|
+
const row = tableInfo.cellsByRow[cell.rowIndex] || [];
|
|
2235
|
+
const nextCell = row[cell.columnIndex + (forward ? 1 : -1)];
|
|
2236
|
+
if (!nextCell) {
|
|
2237
|
+
return false;
|
|
2238
|
+
}
|
|
2239
|
+
this.moveSelectionToCell(view, nextCell);
|
|
2240
|
+
return true;
|
|
2241
|
+
}
|
|
2242
|
+
/** Moves vertically between rows while keeping the current column. */
|
|
2243
|
+
handleArrowVertical(view, forward) {
|
|
2244
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
2245
|
+
if (!tableInfo) {
|
|
2246
|
+
return false;
|
|
2247
|
+
}
|
|
2248
|
+
const cell = this.getCurrentCell(view, tableInfo);
|
|
2249
|
+
if (!cell) {
|
|
2250
|
+
return false;
|
|
2251
|
+
}
|
|
2252
|
+
const nextRow = tableInfo.cellsByRow[cell.rowIndex + (forward ? 1 : -1)];
|
|
2253
|
+
if (!nextRow) {
|
|
2254
|
+
return false;
|
|
2255
|
+
}
|
|
2256
|
+
const nextCell = nextRow[cell.columnIndex];
|
|
2257
|
+
if (!nextCell) {
|
|
2258
|
+
return false;
|
|
2259
|
+
}
|
|
2260
|
+
this.moveSelectionToCell(view, nextCell);
|
|
2261
|
+
return true;
|
|
2262
|
+
}
|
|
2263
|
+
/** Advances downward on Enter and manages the trailing empty row behavior. */
|
|
2264
|
+
handleEnter(view) {
|
|
2265
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
2266
|
+
if (!tableInfo) {
|
|
2267
|
+
return false;
|
|
2268
|
+
}
|
|
2269
|
+
const cell = this.getCurrentCell(view, tableInfo);
|
|
2270
|
+
if (!cell) {
|
|
2271
|
+
return false;
|
|
2272
|
+
}
|
|
2273
|
+
if (cell.rowKind === "body") {
|
|
2274
|
+
const currentRow = tableInfo.bodyCells[cell.rowIndex - 1];
|
|
2275
|
+
if (currentRow && isBodyRowEmpty(currentRow)) {
|
|
2276
|
+
const parsed = normalizeParsedTable(buildTableFromInfo(tableInfo));
|
|
2277
|
+
parsed.rows.splice(cell.rowIndex - 1, 1);
|
|
2278
|
+
const formatted = formatTableMarkdown(parsed);
|
|
2279
|
+
const change = createTableInsert(view.state, tableInfo.from, tableInfo.to, formatted);
|
|
2280
|
+
const anchor = Math.min(change.from + change.insert.length, view.state.doc.length + change.insert.length);
|
|
2281
|
+
view.dispatch({
|
|
2282
|
+
changes: { from: change.from, to: change.to, insert: change.insert },
|
|
2283
|
+
selection: { anchor }
|
|
1564
2284
|
});
|
|
2285
|
+
return true;
|
|
1565
2286
|
}
|
|
1566
2287
|
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
if (cursor >= absStart && cursor <= absEnd) {
|
|
1573
|
-
currentCellIdx = i;
|
|
1574
|
-
break;
|
|
1575
|
-
}
|
|
2288
|
+
if (cell.rowKind === "body" && cell.rowIndex === tableInfo.cellsByRow.length - 1) {
|
|
2289
|
+
const parsed = normalizeParsedTable(buildTableFromInfo(tableInfo));
|
|
2290
|
+
parsed.rows.push(buildEmptyRow(tableInfo.columnCount));
|
|
2291
|
+
this.replaceTable(view, tableInfo, parsed, parsed.rows.length, cell.columnIndex);
|
|
2292
|
+
return true;
|
|
1576
2293
|
}
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
const
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
2294
|
+
this.insertRowBelow(view, tableInfo, cell.rowIndex, cell.columnIndex);
|
|
2295
|
+
return true;
|
|
2296
|
+
}
|
|
2297
|
+
/** Inserts a canonical `<br />` token inside the current table cell. */
|
|
2298
|
+
insertBreakTag(view) {
|
|
2299
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
2300
|
+
if (!tableInfo) {
|
|
2301
|
+
return false;
|
|
2302
|
+
}
|
|
2303
|
+
const selection = view.state.selection.main;
|
|
1586
2304
|
view.dispatch({
|
|
1587
|
-
selection:
|
|
1588
|
-
|
|
1589
|
-
head: selectTo
|
|
1590
|
-
},
|
|
1591
|
-
scrollIntoView: true
|
|
2305
|
+
changes: { from: selection.from, to: selection.to, insert: BREAK_TAG },
|
|
2306
|
+
selection: { anchor: selection.from + BREAK_TAG.length }
|
|
1592
2307
|
});
|
|
1593
2308
|
return true;
|
|
1594
2309
|
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
const
|
|
1603
|
-
const
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
2310
|
+
/** Deletes a whole `<br />` token when backspace or delete hits it. */
|
|
2311
|
+
handleBreakDeletion(view, forward) {
|
|
2312
|
+
const tableInfo = this.getTableAtCursor(view);
|
|
2313
|
+
if (!tableInfo) {
|
|
2314
|
+
return false;
|
|
2315
|
+
}
|
|
2316
|
+
const selection = view.state.selection.main;
|
|
2317
|
+
const cursor = selection.head;
|
|
2318
|
+
for (const range of collectBreakRanges(tableInfo)) {
|
|
2319
|
+
const within = cursor > range.from && cursor < range.to;
|
|
2320
|
+
const matchesBackspace = !forward && cursor === range.to;
|
|
2321
|
+
const matchesDelete = forward && cursor === range.from;
|
|
2322
|
+
const overlapsSelection = !selection.empty && selection.from <= range.from && selection.to >= range.to;
|
|
2323
|
+
if (within || matchesBackspace || matchesDelete || overlapsSelection) {
|
|
2324
|
+
view.dispatch({
|
|
2325
|
+
changes: { from: range.from, to: range.to, insert: "" },
|
|
2326
|
+
selection: { anchor: range.from }
|
|
2327
|
+
});
|
|
2328
|
+
return true;
|
|
1610
2329
|
}
|
|
2330
|
+
}
|
|
2331
|
+
return false;
|
|
2332
|
+
}
|
|
2333
|
+
/** Moves the current selection anchor into a target cell. */
|
|
2334
|
+
moveSelectionToCell(view, cell, offset = 0) {
|
|
2335
|
+
const end = Math.max(cell.contentFrom, cell.contentTo);
|
|
2336
|
+
view.dispatch({
|
|
2337
|
+
selection: { anchor: Math.min(cell.contentFrom + offset, end) },
|
|
2338
|
+
scrollIntoView: true
|
|
1611
2339
|
});
|
|
1612
|
-
return result;
|
|
1613
2340
|
}
|
|
1614
|
-
/**
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
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;
|
|
2341
|
+
/** Returns the table currently containing the editor cursor. */
|
|
2342
|
+
getTableAtCursor(view) {
|
|
2343
|
+
return getTableInfoAtPosition(view.state, view.state.selection.main.head);
|
|
2344
|
+
}
|
|
2345
|
+
/** Returns the active cell under the current selection head. */
|
|
2346
|
+
getCurrentCell(view, tableInfo) {
|
|
2347
|
+
return findCellAtPosition(tableInfo, view.state.selection.main.head);
|
|
1637
2348
|
}
|
|
1638
2349
|
};
|
|
1639
2350
|
var theme6 = createTheme({
|
|
1640
2351
|
default: {
|
|
1641
|
-
|
|
1642
|
-
|
|
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": {
|
|
2352
|
+
".cm-draftly-table-wrapper, .cm-draftly-table-widget": {
|
|
2353
|
+
display: "table",
|
|
1684
2354
|
width: "100%",
|
|
1685
2355
|
borderCollapse: "separate",
|
|
1686
2356
|
borderSpacing: "0",
|
|
1687
|
-
|
|
1688
|
-
overflow: "
|
|
1689
|
-
border: "1px solid var(--color-border, #
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
2357
|
+
position: "relative",
|
|
2358
|
+
overflow: "visible",
|
|
2359
|
+
border: "1px solid var(--color-border, #d7dee7)",
|
|
2360
|
+
borderRadius: "0.75rem",
|
|
2361
|
+
backgroundColor: "var(--color-background, #ffffff)",
|
|
2362
|
+
"& .cm-draftly-table": {
|
|
2363
|
+
width: "100%",
|
|
2364
|
+
borderCollapse: "separate",
|
|
2365
|
+
borderSpacing: "0",
|
|
2366
|
+
tableLayout: "auto"
|
|
2367
|
+
},
|
|
2368
|
+
"& .cm-draftly-table-row": {
|
|
2369
|
+
display: "table-row !important"
|
|
2370
|
+
},
|
|
2371
|
+
"& .cm-draftly-table-header-row": {
|
|
2372
|
+
backgroundColor: "rgba(15, 23, 42, 0.04)"
|
|
2373
|
+
},
|
|
2374
|
+
"& .cm-draftly-table-row-even": {
|
|
2375
|
+
backgroundColor: "rgba(15, 23, 42, 0.02)"
|
|
2376
|
+
},
|
|
2377
|
+
"& .cm-draftly-table-delimiter-row": {
|
|
2378
|
+
display: "none !important"
|
|
2379
|
+
},
|
|
2380
|
+
"& .cm-draftly-table-cell": {
|
|
2381
|
+
display: "table-cell",
|
|
2382
|
+
minWidth: "4rem",
|
|
2383
|
+
minHeight: "2.5rem",
|
|
2384
|
+
height: "2.75rem",
|
|
2385
|
+
padding: "0.5rem 0.875rem",
|
|
2386
|
+
verticalAlign: "top",
|
|
2387
|
+
borderRight: "1px solid var(--color-border, #d7dee7)",
|
|
2388
|
+
borderBottom: "1px solid var(--color-border, #d7dee7)",
|
|
2389
|
+
whiteSpace: "normal",
|
|
2390
|
+
overflowWrap: "break-word",
|
|
2391
|
+
wordBreak: "normal",
|
|
2392
|
+
lineHeight: "1.6"
|
|
2393
|
+
},
|
|
2394
|
+
"& .cm-draftly-table-body-row": {
|
|
2395
|
+
minHeight: "2.75rem"
|
|
2396
|
+
},
|
|
2397
|
+
"& .cm-draftly-table-cell .cm-draftly-code-inline": {
|
|
2398
|
+
whiteSpace: "normal",
|
|
2399
|
+
overflowWrap: "anywhere"
|
|
2400
|
+
},
|
|
2401
|
+
"& .cm-draftly-table-th": {
|
|
2402
|
+
fontWeight: "600",
|
|
2403
|
+
borderBottomWidth: "2px"
|
|
2404
|
+
},
|
|
2405
|
+
"& .cm-draftly-table-cell-last": {
|
|
2406
|
+
borderRight: "none"
|
|
2407
|
+
},
|
|
2408
|
+
"& .cm-draftly-table-row-last .cm-draftly-table-cell": {
|
|
2409
|
+
borderBottom: "none"
|
|
2410
|
+
},
|
|
2411
|
+
"& .cm-draftly-table-cell-center": {
|
|
2412
|
+
textAlign: "center"
|
|
2413
|
+
},
|
|
2414
|
+
"& .cm-draftly-table-cell-right": {
|
|
2415
|
+
textAlign: "right"
|
|
2416
|
+
},
|
|
2417
|
+
"& .cm-draftly-table-break": {
|
|
2418
|
+
display: "inline"
|
|
2419
|
+
},
|
|
2420
|
+
"& .cm-draftly-table-controls-anchor": {
|
|
2421
|
+
position: "absolute",
|
|
2422
|
+
inset: "0",
|
|
2423
|
+
pointerEvents: "none"
|
|
2424
|
+
},
|
|
2425
|
+
"& .cm-draftly-table-control": {
|
|
2426
|
+
position: "absolute",
|
|
2427
|
+
width: "1.75rem",
|
|
2428
|
+
height: "1.75rem",
|
|
2429
|
+
border: "1px solid var(--color-border, #d7dee7)",
|
|
2430
|
+
borderRadius: "999px",
|
|
2431
|
+
backgroundColor: "var(--color-background, #ffffff)",
|
|
2432
|
+
color: "var(--color-text, #0f172a)",
|
|
2433
|
+
boxShadow: "0 10px 24px rgba(15, 23, 42, 0.12)",
|
|
2434
|
+
display: "inline-flex",
|
|
2435
|
+
alignItems: "center",
|
|
2436
|
+
justifyContent: "center",
|
|
2437
|
+
opacity: "0",
|
|
2438
|
+
pointerEvents: "auto",
|
|
2439
|
+
transition: "opacity 120ms ease, transform 120ms ease, background-color 120ms ease"
|
|
2440
|
+
},
|
|
2441
|
+
"& .cm-draftly-table-control:hover": {
|
|
2442
|
+
backgroundColor: "rgba(15, 23, 42, 0.05)"
|
|
2443
|
+
},
|
|
2444
|
+
"& .cm-draftly-table-control-column": {
|
|
2445
|
+
top: "50%",
|
|
2446
|
+
right: "-0.95rem",
|
|
2447
|
+
transform: "translate(0.35rem, -50%)"
|
|
2448
|
+
},
|
|
2449
|
+
"& .cm-draftly-table-control-row": {
|
|
2450
|
+
left: "50%",
|
|
2451
|
+
bottom: "-0.95rem",
|
|
2452
|
+
transform: "translate(-50%, 0.35rem)"
|
|
2453
|
+
},
|
|
2454
|
+
"&:hover .cm-draftly-table-control, &:focus-within .cm-draftly-table-control": {
|
|
2455
|
+
opacity: "1"
|
|
2456
|
+
},
|
|
2457
|
+
"&:hover .cm-draftly-table-control-column, &:focus-within .cm-draftly-table-control-column": {
|
|
2458
|
+
transform: "translate(0, -50%)"
|
|
2459
|
+
},
|
|
2460
|
+
"&:hover .cm-draftly-table-control-row, &:focus-within .cm-draftly-table-control-row": {
|
|
2461
|
+
transform: "translate(-50%, 0)"
|
|
2462
|
+
}
|
|
1748
2463
|
}
|
|
1749
2464
|
},
|
|
1750
2465
|
dark: {
|
|
1751
|
-
".cm-draftly-table-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
2466
|
+
".cm-draftly-table-wrapper, .cm-draftly-table-widget": {
|
|
2467
|
+
borderColor: "var(--color-border, #30363d)",
|
|
2468
|
+
backgroundColor: "var(--color-background, #0d1117)",
|
|
2469
|
+
"& .cm-draftly-table-header-row": {
|
|
2470
|
+
backgroundColor: "rgba(255, 255, 255, 0.05)"
|
|
2471
|
+
},
|
|
2472
|
+
"& .cm-draftly-table-row-even": {
|
|
2473
|
+
backgroundColor: "rgba(255, 255, 255, 0.025)"
|
|
2474
|
+
},
|
|
2475
|
+
"& .cm-draftly-table-cell": {
|
|
2476
|
+
borderColor: "var(--color-border, #30363d)"
|
|
2477
|
+
},
|
|
2478
|
+
"& .cm-draftly-table-control": {
|
|
2479
|
+
borderColor: "var(--color-border, #30363d)",
|
|
2480
|
+
backgroundColor: "var(--color-background, #161b22)",
|
|
2481
|
+
color: "var(--color-text, #e6edf3)",
|
|
2482
|
+
boxShadow: "0 12px 28px rgba(0, 0, 0, 0.35)"
|
|
2483
|
+
},
|
|
2484
|
+
"& .cm-draftly-table-control:hover": {
|
|
2485
|
+
backgroundColor: "rgba(255, 255, 255, 0.08)"
|
|
2486
|
+
}
|
|
1771
2487
|
}
|
|
1772
2488
|
}
|
|
1773
2489
|
});
|
|
@@ -4639,5 +5355,5 @@ var essentialPlugins = [
|
|
|
4639
5355
|
var allPlugins = [...essentialPlugins];
|
|
4640
5356
|
|
|
4641
5357
|
export { CodePlugin, EmojiPlugin, HRPlugin, HTMLPlugin, HeadingPlugin, ImagePlugin, InlinePlugin, LinkPlugin, ListPlugin, MathPlugin, MermaidPlugin, ParagraphPlugin, QuotePlugin, TablePlugin, allPlugins, essentialPlugins };
|
|
4642
|
-
//# sourceMappingURL=chunk-
|
|
4643
|
-
//# sourceMappingURL=chunk-
|
|
5358
|
+
//# sourceMappingURL=chunk-NRPI5O6Y.js.map
|
|
5359
|
+
//# sourceMappingURL=chunk-NRPI5O6Y.js.map
|