autumnnote 1.8.2 → 1.9.1
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 +11 -5
- package/dist/autumnnote.es.js +117 -28
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +117 -28
- package/dist/autumnnote.umd.js.map +1 -1
- package/dist/robots.txt +4 -0
- package/dist/sitemap.xml +18 -0
- package/package.json +6 -2
- package/src/js/core/markdown.js +85 -35
- package/src/js/index.js +1 -1
- package/src/js/module/Clipboard.js +28 -1
- package/src/js/module/ContextMenu.js +1 -1
- package/src/js/module/ImageCropOverlay.js +1 -0
- package/src/js/settings.js +7 -1
- package/types/index.d.ts +4 -2
package/dist/autumnnote.umd.js
CHANGED
|
@@ -1437,6 +1437,8 @@
|
|
|
1437
1437
|
autoSaveRestoreTimeout: 7,
|
|
1438
1438
|
onAutoSaveRestore: null,
|
|
1439
1439
|
markdownShortcuts: true,
|
|
1440
|
+
maxPasteSize: 5 * 1024 * 1024,
|
|
1441
|
+
minImageSize: 20,
|
|
1440
1442
|
bubbleToolbar: false,
|
|
1441
1443
|
bubbleToolbarItems: [
|
|
1442
1444
|
"bold",
|
|
@@ -5307,7 +5309,7 @@
|
|
|
5307
5309
|
* @returns {boolean} `true` if any Markdown-like pattern is present, `false` otherwise.
|
|
5308
5310
|
*/
|
|
5309
5311
|
function isMarkdown(text) {
|
|
5310
|
-
return /^#{1,6} [^\s]|^[ \t]*[-*+] [^\s]|^[ \t]*\d+\. [^\s]|^> [^\s]|^```|^\*{2}[^*\n]+\*{2}/m.test(text);
|
|
5312
|
+
return /^#{1,6} [^\s]|^[ \t]*[-*+] [^\s]|^[ \t]*\d+\. [^\s]|^> [^\s]|^```|^\*{2}[^*\n]+\*{2}/m.test(text) || /^.+\n=+\s*$/m.test(text) || /^.+\n-{2,}\s*$/m.test(text);
|
|
5311
5313
|
}
|
|
5312
5314
|
/**
|
|
5313
5315
|
* Converts a Markdown string to an HTML string.
|
|
@@ -5334,6 +5336,18 @@
|
|
|
5334
5336
|
i++;
|
|
5335
5337
|
continue;
|
|
5336
5338
|
}
|
|
5339
|
+
if (line.trim() && !/^(-{3,}|\*{3,}|_{3,})\s*$/.test(line) && !/^#{1,6} /.test(line) && i + 1 < lines.length) {
|
|
5340
|
+
if (/^=+\s*$/.test(lines[i + 1])) {
|
|
5341
|
+
out.push(`<h1>${_inline(line.trim())}</h1>`);
|
|
5342
|
+
i += 2;
|
|
5343
|
+
continue;
|
|
5344
|
+
}
|
|
5345
|
+
if (/^-{2,}\s*$/.test(lines[i + 1])) {
|
|
5346
|
+
out.push(`<h2>${_inline(line.trim())}</h2>`);
|
|
5347
|
+
i += 2;
|
|
5348
|
+
continue;
|
|
5349
|
+
}
|
|
5350
|
+
}
|
|
5337
5351
|
if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(line)) {
|
|
5338
5352
|
out.push("<hr>");
|
|
5339
5353
|
i++;
|
|
@@ -5356,30 +5370,15 @@
|
|
|
5356
5370
|
continue;
|
|
5357
5371
|
}
|
|
5358
5372
|
if (/^[-*+] /.test(line)) {
|
|
5359
|
-
const
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
while (i < lines.length && /^[-*+] /.test(lines[i])) {
|
|
5363
|
-
if (/^[-*+]\s+\[[ xX]\]\s+/.test(lines[i]) !== isChecklist) break;
|
|
5364
|
-
const content = lines[i].slice(2);
|
|
5365
|
-
if (isChecklist) {
|
|
5366
|
-
const cbMatch = /^\[([ xX])\][ \t]+/.exec(content);
|
|
5367
|
-
const cbHtml = `<input type="checkbox" contenteditable="false"${cbMatch?.[1]?.toLowerCase() === "x" ? " checked" : ""}>`;
|
|
5368
|
-
const textContent = cbMatch ? content.slice(cbMatch[0].length) : content;
|
|
5369
|
-
items.push(`<li>${cbHtml}${_inline(textContent)}</li>`);
|
|
5370
|
-
} else items.push(`<li>${_inline(content)}</li>`);
|
|
5371
|
-
i++;
|
|
5372
|
-
}
|
|
5373
|
-
out.push(`<${listTag}>${items.join("")}</${listTag.split(" ")[0]}>`);
|
|
5373
|
+
const { html: listHtml, endIdx } = _parseListBlock(lines, i);
|
|
5374
|
+
out.push(listHtml);
|
|
5375
|
+
i = endIdx;
|
|
5374
5376
|
continue;
|
|
5375
5377
|
}
|
|
5376
5378
|
if (/^\d+\. /.test(line)) {
|
|
5377
|
-
const
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
i++;
|
|
5381
|
-
}
|
|
5382
|
-
out.push(`<ol>${items.join("")}</ol>`);
|
|
5379
|
+
const { html: listHtml, endIdx } = _parseListBlock(lines, i);
|
|
5380
|
+
out.push(listHtml);
|
|
5381
|
+
i = endIdx;
|
|
5383
5382
|
continue;
|
|
5384
5383
|
}
|
|
5385
5384
|
if (line.trim() === "") {
|
|
@@ -5388,20 +5387,29 @@
|
|
|
5388
5387
|
}
|
|
5389
5388
|
if (/^\|.+\|/.test(line) && i + 1 < lines.length && /^\|[\s|:-]+\|/.test(lines[i + 1])) {
|
|
5390
5389
|
const headerCells = _parseTableRow(line);
|
|
5390
|
+
const alignments = _parseTableRow(lines[i + 1]).map((c) => {
|
|
5391
|
+
if (c.startsWith(":") && c.endsWith(":")) return "center";
|
|
5392
|
+
if (c.endsWith(":")) return "right";
|
|
5393
|
+
if (c.startsWith(":")) return "left";
|
|
5394
|
+
return null;
|
|
5395
|
+
});
|
|
5391
5396
|
i += 2;
|
|
5392
5397
|
const bodyRows = [];
|
|
5393
5398
|
while (i < lines.length && /^\|.+\|/.test(lines[i])) {
|
|
5394
5399
|
bodyRows.push(_parseTableRow(lines[i]));
|
|
5395
5400
|
i++;
|
|
5396
5401
|
}
|
|
5397
|
-
const
|
|
5398
|
-
|
|
5402
|
+
const _cell = (tag, content, align) => {
|
|
5403
|
+
return `<${tag}${align ? ` style="text-align:${align}"` : ""}>${_inline(content)}</${tag}>`;
|
|
5404
|
+
};
|
|
5405
|
+
const thead = `<thead><tr>${headerCells.map((c, idx) => _cell("th", c, alignments[idx])).join("")}</tr></thead>`;
|
|
5406
|
+
const renderRow = (row) => `<tr>${row.map((c, idx) => _cell("td", c, alignments[idx])).join("")}</tr>`;
|
|
5399
5407
|
const tbody = bodyRows.length ? `<tbody>${bodyRows.map(renderRow).join("")}</tbody>` : "";
|
|
5400
5408
|
out.push(`<table>${thead}${tbody}</table>`);
|
|
5401
5409
|
continue;
|
|
5402
5410
|
}
|
|
5403
5411
|
const paraLines = [];
|
|
5404
|
-
while (i < lines.length && lines[i].trim() !== "" && !/^(#{1,6} |> |[-*+] |\d+\. |```|---\s*$|\*{3}\s*$|_{3}\s*$)/.test(lines[i]) && !/^\|.+\|/.test(lines[i])) {
|
|
5412
|
+
while (i < lines.length && lines[i].trim() !== "" && !/^(#{1,6} |> |[-*+] |\d+\. |```|---\s*$|\*{3}\s*$|_{3}\s*$)/.test(lines[i]) && !/^\|.+\|/.test(lines[i]) && !(i + 1 < lines.length && /^=+\s*$/.test(lines[i + 1])) && !(i + 1 < lines.length && /^-{2,}\s*$/.test(lines[i + 1]))) {
|
|
5405
5413
|
paraLines.push(lines[i]);
|
|
5406
5414
|
i++;
|
|
5407
5415
|
}
|
|
@@ -5418,6 +5426,57 @@
|
|
|
5418
5426
|
function _parseTableRow(row) {
|
|
5419
5427
|
return row.replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
|
|
5420
5428
|
}
|
|
5429
|
+
function _parseListBlock(lines, startIdx) {
|
|
5430
|
+
const baseIndent = lines[startIdx].match(/^(\s*)/)[1].length;
|
|
5431
|
+
const isOL = /^\s*\d+\. /.test(lines[startIdx]);
|
|
5432
|
+
const items = [];
|
|
5433
|
+
let firstIsCB = null;
|
|
5434
|
+
let i = startIdx;
|
|
5435
|
+
while (i < lines.length) {
|
|
5436
|
+
const line = lines[i];
|
|
5437
|
+
if (line.trim() === "") break;
|
|
5438
|
+
const indent = line.match(/^(\s*)/)[1].length;
|
|
5439
|
+
if (indent < baseIndent) break;
|
|
5440
|
+
if (indent === baseIndent) {
|
|
5441
|
+
if (!/^\s*(?:[-*+]|\d+\.) /.test(line)) break;
|
|
5442
|
+
if (/^\s*\d+\. /.test(line) !== isOL) break;
|
|
5443
|
+
const raw = isOL ? line.replace(/^\s*\d+\. /, "") : line.replace(/^\s*[-*+] /, "");
|
|
5444
|
+
const isCB = !isOL && /^\[[ xX]\]\s+/.test(raw);
|
|
5445
|
+
if (firstIsCB === null) firstIsCB = isCB;
|
|
5446
|
+
if (isCB !== firstIsCB) break;
|
|
5447
|
+
const checked = isCB && raw[1].toLowerCase() === "x";
|
|
5448
|
+
const text = isCB ? raw.replace(/^\[[ xX]\]\s+/, "") : raw;
|
|
5449
|
+
items.push({
|
|
5450
|
+
text,
|
|
5451
|
+
isCB,
|
|
5452
|
+
checked,
|
|
5453
|
+
sub: ""
|
|
5454
|
+
});
|
|
5455
|
+
i++;
|
|
5456
|
+
} else {
|
|
5457
|
+
if (!items.length) {
|
|
5458
|
+
i++;
|
|
5459
|
+
continue;
|
|
5460
|
+
}
|
|
5461
|
+
if (/^\s*(?:[-*+]|\d+\.) /.test(line)) {
|
|
5462
|
+
const nested = _parseListBlock(lines, i);
|
|
5463
|
+
items[items.length - 1].sub += nested.html;
|
|
5464
|
+
i = nested.endIdx;
|
|
5465
|
+
} else {
|
|
5466
|
+
items[items.length - 1].text += " " + line.trim();
|
|
5467
|
+
i++;
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
const open = isOL ? "<ol>" : !isOL && firstIsCB === true ? "<ul class=\"an-checklist\">" : "<ul>";
|
|
5472
|
+
const close = isOL ? "</ol>" : "</ul>";
|
|
5473
|
+
return {
|
|
5474
|
+
html: `${open}${items.map(({ text, isCB, checked, sub }) => {
|
|
5475
|
+
return `<li>${isCB ? `<input type="checkbox" contenteditable="false"${checked ? " checked" : ""}>` : ""}${_inline(text)}${sub}</li>`;
|
|
5476
|
+
}).join("")}${close}`,
|
|
5477
|
+
endIdx: i
|
|
5478
|
+
};
|
|
5479
|
+
}
|
|
5421
5480
|
function _inline(text) {
|
|
5422
5481
|
text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, src) => `<img src="${_escAttr(src)}" alt="${_escAttr(alt)}" class="an-image">`);
|
|
5423
5482
|
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, href) => `<a href="${_escAttr(href)}">${_esc(label)}</a>`);
|
|
@@ -6969,6 +7028,29 @@
|
|
|
6969
7028
|
return doc.body.innerHTML;
|
|
6970
7029
|
}
|
|
6971
7030
|
/**
|
|
7031
|
+
* Normalizes task lists from external sources (GitHub, GitLab, etc.) so they
|
|
7032
|
+
* pass the sanitiser's `ul.an-checklist` guard. Runs before sanitiseHTML().
|
|
7033
|
+
* @param {string} html
|
|
7034
|
+
* @returns {string}
|
|
7035
|
+
*/
|
|
7036
|
+
_normalizeExternalTaskLists(html) {
|
|
7037
|
+
const doc = new DOMParser().parseFromString(`<body>${html}</body>`, "text/html");
|
|
7038
|
+
for (const cb of doc.querySelectorAll("input[type=\"checkbox\"]")) {
|
|
7039
|
+
const li = cb.closest("li");
|
|
7040
|
+
const ul = li?.closest("ul");
|
|
7041
|
+
if (!li || !ul || ul.classList.contains("an-checklist")) continue;
|
|
7042
|
+
ul.classList.add("an-checklist");
|
|
7043
|
+
cb.removeAttribute("disabled");
|
|
7044
|
+
cb.setAttribute("contenteditable", "false");
|
|
7045
|
+
for (const attr of Array.from(cb.attributes)) if (![
|
|
7046
|
+
"type",
|
|
7047
|
+
"checked",
|
|
7048
|
+
"contenteditable"
|
|
7049
|
+
].includes(attr.name)) cb.removeAttribute(attr.name);
|
|
7050
|
+
}
|
|
7051
|
+
return doc.body.innerHTML;
|
|
7052
|
+
}
|
|
7053
|
+
/**
|
|
6972
7054
|
* Forces the next paste operation to strip all HTML formatting.
|
|
6973
7055
|
* Called by Editor when Ctrl+Shift+V is pressed.
|
|
6974
7056
|
* @param {boolean} val
|
|
@@ -7026,6 +7108,7 @@
|
|
|
7026
7108
|
let html = raw;
|
|
7027
7109
|
if (isWordContent) html = this._cleanWordHtml(html);
|
|
7028
7110
|
else if (isSocialContent) html = this._cleanSocialHtml(html);
|
|
7111
|
+
html = this._normalizeExternalTaskLists(html);
|
|
7029
7112
|
html = sanitiseHTML(html);
|
|
7030
7113
|
if (this.options.pasteStripAttributes) html = this._stripAttributes(html);
|
|
7031
7114
|
execCommand("insertHTML", html);
|
|
@@ -7140,7 +7223,7 @@
|
|
|
7140
7223
|
_compressImage(file) {
|
|
7141
7224
|
const MAX_DIM = 1920;
|
|
7142
7225
|
const QUALITY = .85;
|
|
7143
|
-
return new Promise((resolve) => {
|
|
7226
|
+
return new Promise((resolve, reject) => {
|
|
7144
7227
|
const objectUrl = URL.createObjectURL(file);
|
|
7145
7228
|
const img = new Image();
|
|
7146
7229
|
img.onload = () => {
|
|
@@ -7160,6 +7243,7 @@
|
|
|
7160
7243
|
if (!ctx) {
|
|
7161
7244
|
const reader = new FileReader();
|
|
7162
7245
|
reader.onload = (e) => resolve(e.target.result);
|
|
7246
|
+
reader.onerror = () => reject(/* @__PURE__ */ new Error("FileReader failed"));
|
|
7163
7247
|
reader.readAsDataURL(file);
|
|
7164
7248
|
return;
|
|
7165
7249
|
}
|
|
@@ -7171,6 +7255,7 @@
|
|
|
7171
7255
|
URL.revokeObjectURL(objectUrl);
|
|
7172
7256
|
const reader = new FileReader();
|
|
7173
7257
|
reader.onload = (e) => resolve(e.target.result);
|
|
7258
|
+
reader.onerror = () => reject(/* @__PURE__ */ new Error("FileReader failed"));
|
|
7174
7259
|
reader.readAsDataURL(file);
|
|
7175
7260
|
};
|
|
7176
7261
|
img.src = objectUrl;
|
|
@@ -13892,7 +13977,7 @@
|
|
|
13892
13977
|
};
|
|
13893
13978
|
if (typeof navigator.clipboard.read === "function") navigator.clipboard.read().then((items) => {
|
|
13894
13979
|
for (const item of items) if (item.types.includes("text/html")) {
|
|
13895
|
-
item.getType("text/html").then((blob) => blob.text()).then((html) => doInsert(html, null));
|
|
13980
|
+
item.getType("text/html").then((blob) => blob.text()).then((html) => doInsert(html, null)).catch(() => {});
|
|
13896
13981
|
return;
|
|
13897
13982
|
}
|
|
13898
13983
|
navigator.clipboard.readText().then((text) => doInsert(null, text)).catch(() => {});
|
|
@@ -15017,6 +15102,10 @@
|
|
|
15017
15102
|
canvas.width = Math.round(renderW);
|
|
15018
15103
|
canvas.height = Math.round(renderH);
|
|
15019
15104
|
const ctx = canvas.getContext("2d");
|
|
15105
|
+
if (!ctx) {
|
|
15106
|
+
resolve(null);
|
|
15107
|
+
return;
|
|
15108
|
+
}
|
|
15020
15109
|
try {
|
|
15021
15110
|
ctx.drawImage(source, naturalRect.x, naturalRect.y, naturalRect.width, naturalRect.height, 0, 0, canvas.width, canvas.height);
|
|
15022
15111
|
ctx.getImageData(0, 0, 1, 1);
|
|
@@ -17079,7 +17168,7 @@
|
|
|
17079
17168
|
/** All pre-built button definitions — accessible in every module format including UMD/CJS. */
|
|
17080
17169
|
buttons,
|
|
17081
17170
|
/** Library version */
|
|
17082
|
-
version: "1.
|
|
17171
|
+
version: "1.9.1"
|
|
17083
17172
|
};
|
|
17084
17173
|
/**
|
|
17085
17174
|
* @param {string|Element|NodeList|Element[]} selector
|