autumnnote 1.8.3 → 1.10.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/autumnnote.es.js +196 -27
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +204 -27
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +1 -2
- package/src/js/core/markdown.js +171 -36
- package/src/js/index.js +1 -1
- package/src/js/module/Clipboard.js +25 -0
- package/types/index.d.ts +1 -1
package/dist/autumnnote.es.js
CHANGED
|
@@ -5596,7 +5596,7 @@ function _domToMd(node, depth = 0) {
|
|
|
5596
5596
|
* @returns {boolean} `true` if any Markdown-like pattern is present, `false` otherwise.
|
|
5597
5597
|
*/
|
|
5598
5598
|
function isMarkdown(text) {
|
|
5599
|
-
return /^#{1,6} [^\s]|^[ \t]*[-*+] [^\s]|^[ \t]*\d+\. [^\s]|^> [^\s]|^```|^\*{2}[^*\n]+\*{2}/m.test(text);
|
|
5599
|
+
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) || /^---\s*\n(?:[\s\S]*?\n)?(?:---|\.\.\.)\s*(?:\n|$)/.test(text) || /^\|.+\|[ \t]*\n\|[ \t:|-]+\|/m.test(text);
|
|
5600
5600
|
}
|
|
5601
5601
|
/**
|
|
5602
5602
|
* Converts a Markdown string to an HTML string.
|
|
@@ -5604,7 +5604,12 @@ function isMarkdown(text) {
|
|
|
5604
5604
|
* @returns {string}
|
|
5605
5605
|
*/
|
|
5606
5606
|
function markdownToHTML(text) {
|
|
5607
|
-
|
|
5607
|
+
let lines = text.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
|
|
5608
|
+
lines = _stripFrontmatter(lines);
|
|
5609
|
+
const refs = _extractReferenceDefinitions(lines);
|
|
5610
|
+
lines = refs.clean;
|
|
5611
|
+
_linkDefs = refs.linkDefs;
|
|
5612
|
+
_footnoteIds = refs.footnoteIds;
|
|
5608
5613
|
const out = [];
|
|
5609
5614
|
let i = 0;
|
|
5610
5615
|
while (i < lines.length) {
|
|
@@ -5623,6 +5628,18 @@ function markdownToHTML(text) {
|
|
|
5623
5628
|
i++;
|
|
5624
5629
|
continue;
|
|
5625
5630
|
}
|
|
5631
|
+
if (line.trim() && !/^(-{3,}|\*{3,}|_{3,})\s*$/.test(line) && !/^#{1,6} /.test(line) && i + 1 < lines.length) {
|
|
5632
|
+
if (/^=+\s*$/.test(lines[i + 1])) {
|
|
5633
|
+
out.push(`<h1>${_inline(line.trim())}</h1>`);
|
|
5634
|
+
i += 2;
|
|
5635
|
+
continue;
|
|
5636
|
+
}
|
|
5637
|
+
if (/^-{2,}\s*$/.test(lines[i + 1])) {
|
|
5638
|
+
out.push(`<h2>${_inline(line.trim())}</h2>`);
|
|
5639
|
+
i += 2;
|
|
5640
|
+
continue;
|
|
5641
|
+
}
|
|
5642
|
+
}
|
|
5626
5643
|
if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(line)) {
|
|
5627
5644
|
out.push("<hr>");
|
|
5628
5645
|
i++;
|
|
@@ -5645,30 +5662,15 @@ function markdownToHTML(text) {
|
|
|
5645
5662
|
continue;
|
|
5646
5663
|
}
|
|
5647
5664
|
if (/^[-*+] /.test(line)) {
|
|
5648
|
-
const
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
while (i < lines.length && /^[-*+] /.test(lines[i])) {
|
|
5652
|
-
if (/^[-*+]\s+\[[ xX]\]\s+/.test(lines[i]) !== isChecklist) break;
|
|
5653
|
-
const content = lines[i].slice(2);
|
|
5654
|
-
if (isChecklist) {
|
|
5655
|
-
const cbMatch = /^\[([ xX])\][ \t]+/.exec(content);
|
|
5656
|
-
const cbHtml = `<input type="checkbox" contenteditable="false"${cbMatch?.[1]?.toLowerCase() === "x" ? " checked" : ""}>`;
|
|
5657
|
-
const textContent = cbMatch ? content.slice(cbMatch[0].length) : content;
|
|
5658
|
-
items.push(`<li>${cbHtml}${_inline(textContent)}</li>`);
|
|
5659
|
-
} else items.push(`<li>${_inline(content)}</li>`);
|
|
5660
|
-
i++;
|
|
5661
|
-
}
|
|
5662
|
-
out.push(`<${listTag}>${items.join("")}</${listTag.split(" ")[0]}>`);
|
|
5665
|
+
const { html: listHtml, endIdx } = _parseListBlock(lines, i);
|
|
5666
|
+
out.push(listHtml);
|
|
5667
|
+
i = endIdx;
|
|
5663
5668
|
continue;
|
|
5664
5669
|
}
|
|
5665
5670
|
if (/^\d+\. /.test(line)) {
|
|
5666
|
-
const
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
i++;
|
|
5670
|
-
}
|
|
5671
|
-
out.push(`<ol>${items.join("")}</ol>`);
|
|
5671
|
+
const { html: listHtml, endIdx } = _parseListBlock(lines, i);
|
|
5672
|
+
out.push(listHtml);
|
|
5673
|
+
i = endIdx;
|
|
5672
5674
|
continue;
|
|
5673
5675
|
}
|
|
5674
5676
|
if (line.trim() === "") {
|
|
@@ -5677,20 +5679,29 @@ function markdownToHTML(text) {
|
|
|
5677
5679
|
}
|
|
5678
5680
|
if (/^\|.+\|/.test(line) && i + 1 < lines.length && /^\|[\s|:-]+\|/.test(lines[i + 1])) {
|
|
5679
5681
|
const headerCells = _parseTableRow(line);
|
|
5682
|
+
const alignments = _parseTableRow(lines[i + 1]).map((c) => {
|
|
5683
|
+
if (c.startsWith(":") && c.endsWith(":")) return "center";
|
|
5684
|
+
if (c.endsWith(":")) return "right";
|
|
5685
|
+
if (c.startsWith(":")) return "left";
|
|
5686
|
+
return null;
|
|
5687
|
+
});
|
|
5680
5688
|
i += 2;
|
|
5681
5689
|
const bodyRows = [];
|
|
5682
5690
|
while (i < lines.length && /^\|.+\|/.test(lines[i])) {
|
|
5683
5691
|
bodyRows.push(_parseTableRow(lines[i]));
|
|
5684
5692
|
i++;
|
|
5685
5693
|
}
|
|
5686
|
-
const
|
|
5687
|
-
|
|
5694
|
+
const _cell = (tag, content, align) => {
|
|
5695
|
+
return `<${tag}${align ? ` style="text-align:${align}"` : ""}>${_inline(content)}</${tag}>`;
|
|
5696
|
+
};
|
|
5697
|
+
const thead = `<thead><tr>${headerCells.map((c, idx) => _cell("th", c, alignments[idx])).join("")}</tr></thead>`;
|
|
5698
|
+
const renderRow = (row) => `<tr>${row.map((c, idx) => _cell("td", c, alignments[idx])).join("")}</tr>`;
|
|
5688
5699
|
const tbody = bodyRows.length ? `<tbody>${bodyRows.map(renderRow).join("")}</tbody>` : "";
|
|
5689
5700
|
out.push(`<table>${thead}${tbody}</table>`);
|
|
5690
5701
|
continue;
|
|
5691
5702
|
}
|
|
5692
5703
|
const paraLines = [];
|
|
5693
|
-
while (i < lines.length && lines[i].trim() !== "" && !/^(#{1,6} |> |[-*+] |\d+\. |```|---\s*$|\*{3}\s*$|_{3}\s*$)/.test(lines[i]) && !/^\|.+\|/.test(lines[i])) {
|
|
5704
|
+
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]))) {
|
|
5694
5705
|
paraLines.push(lines[i]);
|
|
5695
5706
|
i++;
|
|
5696
5707
|
}
|
|
@@ -5698,6 +5709,76 @@ function markdownToHTML(text) {
|
|
|
5698
5709
|
}
|
|
5699
5710
|
return out.join("");
|
|
5700
5711
|
}
|
|
5712
|
+
/** Reference-link and footnote definitions collected per markdownToHTML() call. */
|
|
5713
|
+
var _linkDefs = /* @__PURE__ */ new Map();
|
|
5714
|
+
var _footnoteIds = /* @__PURE__ */ new Set();
|
|
5715
|
+
/**
|
|
5716
|
+
* Strips a leading YAML frontmatter block (--- ... --- or --- ... ...) from
|
|
5717
|
+
* the line array, only when it is the very first line and the enclosed body
|
|
5718
|
+
* looks like YAML (key: value / list items / indented continuations) — this
|
|
5719
|
+
* disambiguates real frontmatter from a horizontal rule followed by prose.
|
|
5720
|
+
* @param {string[]} lines
|
|
5721
|
+
* @returns {string[]}
|
|
5722
|
+
*/
|
|
5723
|
+
function _stripFrontmatter(lines) {
|
|
5724
|
+
if ((lines[0] || "").trim() !== "---") return lines;
|
|
5725
|
+
let closeIdx = -1;
|
|
5726
|
+
for (let j = 1; j < lines.length; j++) {
|
|
5727
|
+
const t = lines[j].trim();
|
|
5728
|
+
if (t === "---" || t === "...") {
|
|
5729
|
+
closeIdx = j;
|
|
5730
|
+
break;
|
|
5731
|
+
}
|
|
5732
|
+
}
|
|
5733
|
+
if (closeIdx === -1) return lines;
|
|
5734
|
+
if (!lines.slice(1, closeIdx).every((l) => l.trim() === "" || /^[ \t]*[\w$.-]+\s*:(\s|$)/.test(l) || /^[ \t]*-\s+\S/.test(l) || /^[ \t]+\S/.test(l))) return lines;
|
|
5735
|
+
let start = closeIdx + 1;
|
|
5736
|
+
if (lines[start] !== void 0 && lines[start].trim() === "") start++;
|
|
5737
|
+
return lines.slice(start);
|
|
5738
|
+
}
|
|
5739
|
+
/**
|
|
5740
|
+
* Extracts GFM reference-link definitions (`[ref]: url "title"`) and footnote
|
|
5741
|
+
* definitions (`[^id]: text`) from the line array, skipping fenced code
|
|
5742
|
+
* regions. Returns the definition-free line array plus lookup maps.
|
|
5743
|
+
* @param {string[]} lines
|
|
5744
|
+
* @returns {{ clean: string[], linkDefs: Map<string, {href: string, title?: string}>, footnoteIds: Set<string> }}
|
|
5745
|
+
*/
|
|
5746
|
+
function _extractReferenceDefinitions(lines) {
|
|
5747
|
+
const linkDefs = /* @__PURE__ */ new Map();
|
|
5748
|
+
const footnoteIds = /* @__PURE__ */ new Set();
|
|
5749
|
+
const clean = [];
|
|
5750
|
+
let inFence = false;
|
|
5751
|
+
const linkDefRe = /^\[([^\]]+)\]:\s*(\S+)(?:\s+"([^"]*)")?\s*$/;
|
|
5752
|
+
const footnoteDefRe = /^\[\^([^\]]+)\]:\s*(.+)$/;
|
|
5753
|
+
for (const line of lines) {
|
|
5754
|
+
if (/^```/.test(line)) {
|
|
5755
|
+
inFence = !inFence;
|
|
5756
|
+
clean.push(line);
|
|
5757
|
+
continue;
|
|
5758
|
+
}
|
|
5759
|
+
if (!inFence) {
|
|
5760
|
+
const fm = footnoteDefRe.exec(line);
|
|
5761
|
+
if (fm) {
|
|
5762
|
+
footnoteIds.add(fm[1]);
|
|
5763
|
+
continue;
|
|
5764
|
+
}
|
|
5765
|
+
const lm = linkDefRe.exec(line);
|
|
5766
|
+
if (lm) {
|
|
5767
|
+
linkDefs.set(lm[1].trim().toLowerCase(), {
|
|
5768
|
+
href: lm[2],
|
|
5769
|
+
title: lm[3]
|
|
5770
|
+
});
|
|
5771
|
+
continue;
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5774
|
+
clean.push(line);
|
|
5775
|
+
}
|
|
5776
|
+
return {
|
|
5777
|
+
clean,
|
|
5778
|
+
linkDefs,
|
|
5779
|
+
footnoteIds
|
|
5780
|
+
};
|
|
5781
|
+
}
|
|
5701
5782
|
/**
|
|
5702
5783
|
* Splits a GFM table row string into trimmed cell strings.
|
|
5703
5784
|
* '| a | b | c |' → ['a', 'b', 'c']
|
|
@@ -5707,9 +5788,73 @@ function markdownToHTML(text) {
|
|
|
5707
5788
|
function _parseTableRow(row) {
|
|
5708
5789
|
return row.replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
|
|
5709
5790
|
}
|
|
5791
|
+
function _parseListBlock(lines, startIdx) {
|
|
5792
|
+
const baseIndent = lines[startIdx].match(/^(\s*)/)[1].length;
|
|
5793
|
+
const isOL = /^\s*\d+\. /.test(lines[startIdx]);
|
|
5794
|
+
const items = [];
|
|
5795
|
+
let firstIsCB = null;
|
|
5796
|
+
let i = startIdx;
|
|
5797
|
+
while (i < lines.length) {
|
|
5798
|
+
const line = lines[i];
|
|
5799
|
+
if (line.trim() === "") break;
|
|
5800
|
+
const indent = line.match(/^(\s*)/)[1].length;
|
|
5801
|
+
if (indent < baseIndent) break;
|
|
5802
|
+
if (indent === baseIndent) {
|
|
5803
|
+
if (!/^\s*(?:[-*+]|\d+\.) /.test(line)) break;
|
|
5804
|
+
if (/^\s*\d+\. /.test(line) !== isOL) break;
|
|
5805
|
+
const raw = isOL ? line.replace(/^\s*\d+\. /, "") : line.replace(/^\s*[-*+] /, "");
|
|
5806
|
+
const isCB = !isOL && /^\[[ xX]\]\s+/.test(raw);
|
|
5807
|
+
if (firstIsCB === null) firstIsCB = isCB;
|
|
5808
|
+
if (isCB !== firstIsCB) break;
|
|
5809
|
+
const checked = isCB && raw[1].toLowerCase() === "x";
|
|
5810
|
+
const text = isCB ? raw.replace(/^\[[ xX]\]\s+/, "") : raw;
|
|
5811
|
+
items.push({
|
|
5812
|
+
text,
|
|
5813
|
+
isCB,
|
|
5814
|
+
checked,
|
|
5815
|
+
sub: ""
|
|
5816
|
+
});
|
|
5817
|
+
i++;
|
|
5818
|
+
} else {
|
|
5819
|
+
if (!items.length) {
|
|
5820
|
+
i++;
|
|
5821
|
+
continue;
|
|
5822
|
+
}
|
|
5823
|
+
if (/^\s*(?:[-*+]|\d+\.) /.test(line)) {
|
|
5824
|
+
const nested = _parseListBlock(lines, i);
|
|
5825
|
+
items[items.length - 1].sub += nested.html;
|
|
5826
|
+
i = nested.endIdx;
|
|
5827
|
+
} else {
|
|
5828
|
+
items[items.length - 1].text += " " + line.trim();
|
|
5829
|
+
i++;
|
|
5830
|
+
}
|
|
5831
|
+
}
|
|
5832
|
+
}
|
|
5833
|
+
const open = isOL ? "<ol>" : !isOL && firstIsCB === true ? "<ul class=\"an-checklist\">" : "<ul>";
|
|
5834
|
+
const close = isOL ? "</ol>" : "</ul>";
|
|
5835
|
+
return {
|
|
5836
|
+
html: `${open}${items.map(({ text, isCB, checked, sub }) => {
|
|
5837
|
+
return `<li>${isCB ? `<input type="checkbox" contenteditable="false"${checked ? " checked" : ""}>` : ""}${_inline(text)}${sub}</li>`;
|
|
5838
|
+
}).join("")}${close}`,
|
|
5839
|
+
endIdx: i
|
|
5840
|
+
};
|
|
5841
|
+
}
|
|
5710
5842
|
function _inline(text) {
|
|
5711
5843
|
text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, src) => `<img src="${_escAttr(src)}" alt="${_escAttr(alt)}" class="an-image">`);
|
|
5712
5844
|
text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, href) => `<a href="${_escAttr(href)}">${_esc(label)}</a>`);
|
|
5845
|
+
text = text.replace(/\[([^\]]+)\]\[([^\]]*)\]/g, (m, label, ref) => {
|
|
5846
|
+
const def = _linkDefs.get((ref || label).trim().toLowerCase());
|
|
5847
|
+
if (!def) return m;
|
|
5848
|
+
const titleAttr = def.title ? ` title="${_escAttr(def.title)}"` : "";
|
|
5849
|
+
return `<a href="${_escAttr(def.href)}"${titleAttr}>${_esc(label)}</a>`;
|
|
5850
|
+
});
|
|
5851
|
+
text = text.replace(/\[([^\]]+)\]/g, (m, label) => {
|
|
5852
|
+
const def = _linkDefs.get(label.trim().toLowerCase());
|
|
5853
|
+
if (!def) return m;
|
|
5854
|
+
const titleAttr = def.title ? ` title="${_escAttr(def.title)}"` : "";
|
|
5855
|
+
return `<a href="${_escAttr(def.href)}"${titleAttr}>${_esc(label)}</a>`;
|
|
5856
|
+
});
|
|
5857
|
+
text = text.replace(/\[\^([^\]]+)\]/g, (m, id) => _footnoteIds.has(id) ? `<sup>[${_esc(id)}]</sup>` : m);
|
|
5713
5858
|
text = text.replace(/\*{3}([^*\n]+?)\*{3}/g, (_, c) => `<strong><em>${_esc(c)}</em></strong>`);
|
|
5714
5859
|
text = text.replace(/_{3}([^_\n]+?)_{3}/g, (_, c) => `<strong><em>${_esc(c)}</em></strong>`);
|
|
5715
5860
|
text = text.replace(/\*{2}([^*\n]+?)\*{2}/g, (_, c) => `<strong>${_esc(c)}</strong>`);
|
|
@@ -7258,6 +7403,29 @@ var Clipboard = class {
|
|
|
7258
7403
|
return doc.body.innerHTML;
|
|
7259
7404
|
}
|
|
7260
7405
|
/**
|
|
7406
|
+
* Normalizes task lists from external sources (GitHub, GitLab, etc.) so they
|
|
7407
|
+
* pass the sanitiser's `ul.an-checklist` guard. Runs before sanitiseHTML().
|
|
7408
|
+
* @param {string} html
|
|
7409
|
+
* @returns {string}
|
|
7410
|
+
*/
|
|
7411
|
+
_normalizeExternalTaskLists(html) {
|
|
7412
|
+
const doc = new DOMParser().parseFromString(`<body>${html}</body>`, "text/html");
|
|
7413
|
+
for (const cb of doc.querySelectorAll("input[type=\"checkbox\"]")) {
|
|
7414
|
+
const li = cb.closest("li");
|
|
7415
|
+
const ul = li?.closest("ul");
|
|
7416
|
+
if (!li || !ul || ul.classList.contains("an-checklist")) continue;
|
|
7417
|
+
ul.classList.add("an-checklist");
|
|
7418
|
+
cb.removeAttribute("disabled");
|
|
7419
|
+
cb.setAttribute("contenteditable", "false");
|
|
7420
|
+
for (const attr of Array.from(cb.attributes)) if (![
|
|
7421
|
+
"type",
|
|
7422
|
+
"checked",
|
|
7423
|
+
"contenteditable"
|
|
7424
|
+
].includes(attr.name)) cb.removeAttribute(attr.name);
|
|
7425
|
+
}
|
|
7426
|
+
return doc.body.innerHTML;
|
|
7427
|
+
}
|
|
7428
|
+
/**
|
|
7261
7429
|
* Forces the next paste operation to strip all HTML formatting.
|
|
7262
7430
|
* Called by Editor when Ctrl+Shift+V is pressed.
|
|
7263
7431
|
* @param {boolean} val
|
|
@@ -7315,6 +7483,7 @@ var Clipboard = class {
|
|
|
7315
7483
|
let html = raw;
|
|
7316
7484
|
if (isWordContent) html = this._cleanWordHtml(html);
|
|
7317
7485
|
else if (isSocialContent) html = this._cleanSocialHtml(html);
|
|
7486
|
+
html = this._normalizeExternalTaskLists(html);
|
|
7318
7487
|
html = sanitiseHTML(html);
|
|
7319
7488
|
if (this.options.pasteStripAttributes) html = this._stripAttributes(html);
|
|
7320
7489
|
execCommand("insertHTML", html);
|
|
@@ -17500,7 +17669,7 @@ var AutumnNote = {
|
|
|
17500
17669
|
/** All pre-built button definitions — accessible in every module format including UMD/CJS. */
|
|
17501
17670
|
buttons,
|
|
17502
17671
|
/** Library version */
|
|
17503
|
-
version: "1.
|
|
17672
|
+
version: "1.10.0"
|
|
17504
17673
|
};
|
|
17505
17674
|
/**
|
|
17506
17675
|
* @param {string|Element|NodeList|Element[]} selector
|