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.
@@ -5309,7 +5309,7 @@
5309
5309
  * @returns {boolean} `true` if any Markdown-like pattern is present, `false` otherwise.
5310
5310
  */
5311
5311
  function isMarkdown(text) {
5312
- 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) || /^---\s*\n(?:[\s\S]*?\n)?(?:---|\.\.\.)\s*(?:\n|$)/.test(text) || /^\|.+\|[ \t]*\n\|[ \t:|-]+\|/m.test(text);
5313
5313
  }
5314
5314
  /**
5315
5315
  * Converts a Markdown string to an HTML string.
@@ -5317,7 +5317,12 @@
5317
5317
  * @returns {string}
5318
5318
  */
5319
5319
  function markdownToHTML(text) {
5320
- const lines = text.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
5320
+ let lines = text.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
5321
+ lines = _stripFrontmatter(lines);
5322
+ const refs = _extractReferenceDefinitions(lines);
5323
+ lines = refs.clean;
5324
+ _linkDefs = refs.linkDefs;
5325
+ _footnoteIds = refs.footnoteIds;
5321
5326
  const out = [];
5322
5327
  let i = 0;
5323
5328
  while (i < lines.length) {
@@ -5336,6 +5341,18 @@
5336
5341
  i++;
5337
5342
  continue;
5338
5343
  }
5344
+ if (line.trim() && !/^(-{3,}|\*{3,}|_{3,})\s*$/.test(line) && !/^#{1,6} /.test(line) && i + 1 < lines.length) {
5345
+ if (/^=+\s*$/.test(lines[i + 1])) {
5346
+ out.push(`<h1>${_inline(line.trim())}</h1>`);
5347
+ i += 2;
5348
+ continue;
5349
+ }
5350
+ if (/^-{2,}\s*$/.test(lines[i + 1])) {
5351
+ out.push(`<h2>${_inline(line.trim())}</h2>`);
5352
+ i += 2;
5353
+ continue;
5354
+ }
5355
+ }
5339
5356
  if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(line)) {
5340
5357
  out.push("<hr>");
5341
5358
  i++;
@@ -5358,30 +5375,15 @@
5358
5375
  continue;
5359
5376
  }
5360
5377
  if (/^[-*+] /.test(line)) {
5361
- const items = [];
5362
- const isChecklist = /^[-*+]\s+\[[ xX]\]\s+/.test(line);
5363
- const listTag = isChecklist ? "ul class=\"an-checklist\"" : "ul";
5364
- while (i < lines.length && /^[-*+] /.test(lines[i])) {
5365
- if (/^[-*+]\s+\[[ xX]\]\s+/.test(lines[i]) !== isChecklist) break;
5366
- const content = lines[i].slice(2);
5367
- if (isChecklist) {
5368
- const cbMatch = /^\[([ xX])\][ \t]+/.exec(content);
5369
- const cbHtml = `<input type="checkbox" contenteditable="false"${cbMatch?.[1]?.toLowerCase() === "x" ? " checked" : ""}>`;
5370
- const textContent = cbMatch ? content.slice(cbMatch[0].length) : content;
5371
- items.push(`<li>${cbHtml}${_inline(textContent)}</li>`);
5372
- } else items.push(`<li>${_inline(content)}</li>`);
5373
- i++;
5374
- }
5375
- out.push(`<${listTag}>${items.join("")}</${listTag.split(" ")[0]}>`);
5378
+ const { html: listHtml, endIdx } = _parseListBlock(lines, i);
5379
+ out.push(listHtml);
5380
+ i = endIdx;
5376
5381
  continue;
5377
5382
  }
5378
5383
  if (/^\d+\. /.test(line)) {
5379
- const items = [];
5380
- while (i < lines.length && /^\d+\. /.test(lines[i])) {
5381
- items.push(`<li>${_inline(lines[i].replace(/^\d+\. /, ""))}</li>`);
5382
- i++;
5383
- }
5384
- out.push(`<ol>${items.join("")}</ol>`);
5384
+ const { html: listHtml, endIdx } = _parseListBlock(lines, i);
5385
+ out.push(listHtml);
5386
+ i = endIdx;
5385
5387
  continue;
5386
5388
  }
5387
5389
  if (line.trim() === "") {
@@ -5390,20 +5392,29 @@
5390
5392
  }
5391
5393
  if (/^\|.+\|/.test(line) && i + 1 < lines.length && /^\|[\s|:-]+\|/.test(lines[i + 1])) {
5392
5394
  const headerCells = _parseTableRow(line);
5395
+ const alignments = _parseTableRow(lines[i + 1]).map((c) => {
5396
+ if (c.startsWith(":") && c.endsWith(":")) return "center";
5397
+ if (c.endsWith(":")) return "right";
5398
+ if (c.startsWith(":")) return "left";
5399
+ return null;
5400
+ });
5393
5401
  i += 2;
5394
5402
  const bodyRows = [];
5395
5403
  while (i < lines.length && /^\|.+\|/.test(lines[i])) {
5396
5404
  bodyRows.push(_parseTableRow(lines[i]));
5397
5405
  i++;
5398
5406
  }
5399
- const thead = `<thead><tr>${headerCells.map((c) => `<th>${_inline(c)}</th>`).join("")}</tr></thead>`;
5400
- const renderRow = (row) => `<tr>${row.map((c) => `<td>${_inline(c)}</td>`).join("")}</tr>`;
5407
+ const _cell = (tag, content, align) => {
5408
+ return `<${tag}${align ? ` style="text-align:${align}"` : ""}>${_inline(content)}</${tag}>`;
5409
+ };
5410
+ const thead = `<thead><tr>${headerCells.map((c, idx) => _cell("th", c, alignments[idx])).join("")}</tr></thead>`;
5411
+ const renderRow = (row) => `<tr>${row.map((c, idx) => _cell("td", c, alignments[idx])).join("")}</tr>`;
5401
5412
  const tbody = bodyRows.length ? `<tbody>${bodyRows.map(renderRow).join("")}</tbody>` : "";
5402
5413
  out.push(`<table>${thead}${tbody}</table>`);
5403
5414
  continue;
5404
5415
  }
5405
5416
  const paraLines = [];
5406
- while (i < lines.length && lines[i].trim() !== "" && !/^(#{1,6} |> |[-*+] |\d+\. |```|---\s*$|\*{3}\s*$|_{3}\s*$)/.test(lines[i]) && !/^\|.+\|/.test(lines[i])) {
5417
+ 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]))) {
5407
5418
  paraLines.push(lines[i]);
5408
5419
  i++;
5409
5420
  }
@@ -5411,6 +5422,76 @@
5411
5422
  }
5412
5423
  return out.join("");
5413
5424
  }
5425
+ /** Reference-link and footnote definitions collected per markdownToHTML() call. */
5426
+ var _linkDefs = /* @__PURE__ */ new Map();
5427
+ var _footnoteIds = /* @__PURE__ */ new Set();
5428
+ /**
5429
+ * Strips a leading YAML frontmatter block (--- ... --- or --- ... ...) from
5430
+ * the line array, only when it is the very first line and the enclosed body
5431
+ * looks like YAML (key: value / list items / indented continuations) — this
5432
+ * disambiguates real frontmatter from a horizontal rule followed by prose.
5433
+ * @param {string[]} lines
5434
+ * @returns {string[]}
5435
+ */
5436
+ function _stripFrontmatter(lines) {
5437
+ if ((lines[0] || "").trim() !== "---") return lines;
5438
+ let closeIdx = -1;
5439
+ for (let j = 1; j < lines.length; j++) {
5440
+ const t = lines[j].trim();
5441
+ if (t === "---" || t === "...") {
5442
+ closeIdx = j;
5443
+ break;
5444
+ }
5445
+ }
5446
+ if (closeIdx === -1) return lines;
5447
+ 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;
5448
+ let start = closeIdx + 1;
5449
+ if (lines[start] !== void 0 && lines[start].trim() === "") start++;
5450
+ return lines.slice(start);
5451
+ }
5452
+ /**
5453
+ * Extracts GFM reference-link definitions (`[ref]: url "title"`) and footnote
5454
+ * definitions (`[^id]: text`) from the line array, skipping fenced code
5455
+ * regions. Returns the definition-free line array plus lookup maps.
5456
+ * @param {string[]} lines
5457
+ * @returns {{ clean: string[], linkDefs: Map<string, {href: string, title?: string}>, footnoteIds: Set<string> }}
5458
+ */
5459
+ function _extractReferenceDefinitions(lines) {
5460
+ const linkDefs = /* @__PURE__ */ new Map();
5461
+ const footnoteIds = /* @__PURE__ */ new Set();
5462
+ const clean = [];
5463
+ let inFence = false;
5464
+ const linkDefRe = /^\[([^\]]+)\]:\s*(\S+)(?:\s+"([^"]*)")?\s*$/;
5465
+ const footnoteDefRe = /^\[\^([^\]]+)\]:\s*(.+)$/;
5466
+ for (const line of lines) {
5467
+ if (/^```/.test(line)) {
5468
+ inFence = !inFence;
5469
+ clean.push(line);
5470
+ continue;
5471
+ }
5472
+ if (!inFence) {
5473
+ const fm = footnoteDefRe.exec(line);
5474
+ if (fm) {
5475
+ footnoteIds.add(fm[1]);
5476
+ continue;
5477
+ }
5478
+ const lm = linkDefRe.exec(line);
5479
+ if (lm) {
5480
+ linkDefs.set(lm[1].trim().toLowerCase(), {
5481
+ href: lm[2],
5482
+ title: lm[3]
5483
+ });
5484
+ continue;
5485
+ }
5486
+ }
5487
+ clean.push(line);
5488
+ }
5489
+ return {
5490
+ clean,
5491
+ linkDefs,
5492
+ footnoteIds
5493
+ };
5494
+ }
5414
5495
  /**
5415
5496
  * Splits a GFM table row string into trimmed cell strings.
5416
5497
  * '| a | b | c |' → ['a', 'b', 'c']
@@ -5420,9 +5501,73 @@
5420
5501
  function _parseTableRow(row) {
5421
5502
  return row.replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
5422
5503
  }
5504
+ function _parseListBlock(lines, startIdx) {
5505
+ const baseIndent = lines[startIdx].match(/^(\s*)/)[1].length;
5506
+ const isOL = /^\s*\d+\. /.test(lines[startIdx]);
5507
+ const items = [];
5508
+ let firstIsCB = null;
5509
+ let i = startIdx;
5510
+ while (i < lines.length) {
5511
+ const line = lines[i];
5512
+ if (line.trim() === "") break;
5513
+ const indent = line.match(/^(\s*)/)[1].length;
5514
+ if (indent < baseIndent) break;
5515
+ if (indent === baseIndent) {
5516
+ if (!/^\s*(?:[-*+]|\d+\.) /.test(line)) break;
5517
+ if (/^\s*\d+\. /.test(line) !== isOL) break;
5518
+ const raw = isOL ? line.replace(/^\s*\d+\. /, "") : line.replace(/^\s*[-*+] /, "");
5519
+ const isCB = !isOL && /^\[[ xX]\]\s+/.test(raw);
5520
+ if (firstIsCB === null) firstIsCB = isCB;
5521
+ if (isCB !== firstIsCB) break;
5522
+ const checked = isCB && raw[1].toLowerCase() === "x";
5523
+ const text = isCB ? raw.replace(/^\[[ xX]\]\s+/, "") : raw;
5524
+ items.push({
5525
+ text,
5526
+ isCB,
5527
+ checked,
5528
+ sub: ""
5529
+ });
5530
+ i++;
5531
+ } else {
5532
+ if (!items.length) {
5533
+ i++;
5534
+ continue;
5535
+ }
5536
+ if (/^\s*(?:[-*+]|\d+\.) /.test(line)) {
5537
+ const nested = _parseListBlock(lines, i);
5538
+ items[items.length - 1].sub += nested.html;
5539
+ i = nested.endIdx;
5540
+ } else {
5541
+ items[items.length - 1].text += " " + line.trim();
5542
+ i++;
5543
+ }
5544
+ }
5545
+ }
5546
+ const open = isOL ? "<ol>" : !isOL && firstIsCB === true ? "<ul class=\"an-checklist\">" : "<ul>";
5547
+ const close = isOL ? "</ol>" : "</ul>";
5548
+ return {
5549
+ html: `${open}${items.map(({ text, isCB, checked, sub }) => {
5550
+ return `<li>${isCB ? `<input type="checkbox" contenteditable="false"${checked ? " checked" : ""}>` : ""}${_inline(text)}${sub}</li>`;
5551
+ }).join("")}${close}`,
5552
+ endIdx: i
5553
+ };
5554
+ }
5423
5555
  function _inline(text) {
5424
5556
  text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, src) => `<img src="${_escAttr(src)}" alt="${_escAttr(alt)}" class="an-image">`);
5425
5557
  text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, href) => `<a href="${_escAttr(href)}">${_esc(label)}</a>`);
5558
+ text = text.replace(/\[([^\]]+)\]\[([^\]]*)\]/g, (m, label, ref) => {
5559
+ const def = _linkDefs.get((ref || label).trim().toLowerCase());
5560
+ if (!def) return m;
5561
+ const titleAttr = def.title ? ` title="${_escAttr(def.title)}"` : "";
5562
+ return `<a href="${_escAttr(def.href)}"${titleAttr}>${_esc(label)}</a>`;
5563
+ });
5564
+ text = text.replace(/\[([^\]]+)\]/g, (m, label) => {
5565
+ const def = _linkDefs.get(label.trim().toLowerCase());
5566
+ if (!def) return m;
5567
+ const titleAttr = def.title ? ` title="${_escAttr(def.title)}"` : "";
5568
+ return `<a href="${_escAttr(def.href)}"${titleAttr}>${_esc(label)}</a>`;
5569
+ });
5570
+ text = text.replace(/\[\^([^\]]+)\]/g, (m, id) => _footnoteIds.has(id) ? `<sup>[${_esc(id)}]</sup>` : m);
5426
5571
  text = text.replace(/\*{3}([^*\n]+?)\*{3}/g, (_, c) => `<strong><em>${_esc(c)}</em></strong>`);
5427
5572
  text = text.replace(/_{3}([^_\n]+?)_{3}/g, (_, c) => `<strong><em>${_esc(c)}</em></strong>`);
5428
5573
  text = text.replace(/\*{2}([^*\n]+?)\*{2}/g, (_, c) => `<strong>${_esc(c)}</strong>`);
@@ -6971,6 +7116,29 @@
6971
7116
  return doc.body.innerHTML;
6972
7117
  }
6973
7118
  /**
7119
+ * Normalizes task lists from external sources (GitHub, GitLab, etc.) so they
7120
+ * pass the sanitiser's `ul.an-checklist` guard. Runs before sanitiseHTML().
7121
+ * @param {string} html
7122
+ * @returns {string}
7123
+ */
7124
+ _normalizeExternalTaskLists(html) {
7125
+ const doc = new DOMParser().parseFromString(`<body>${html}</body>`, "text/html");
7126
+ for (const cb of doc.querySelectorAll("input[type=\"checkbox\"]")) {
7127
+ const li = cb.closest("li");
7128
+ const ul = li?.closest("ul");
7129
+ if (!li || !ul || ul.classList.contains("an-checklist")) continue;
7130
+ ul.classList.add("an-checklist");
7131
+ cb.removeAttribute("disabled");
7132
+ cb.setAttribute("contenteditable", "false");
7133
+ for (const attr of Array.from(cb.attributes)) if (![
7134
+ "type",
7135
+ "checked",
7136
+ "contenteditable"
7137
+ ].includes(attr.name)) cb.removeAttribute(attr.name);
7138
+ }
7139
+ return doc.body.innerHTML;
7140
+ }
7141
+ /**
6974
7142
  * Forces the next paste operation to strip all HTML formatting.
6975
7143
  * Called by Editor when Ctrl+Shift+V is pressed.
6976
7144
  * @param {boolean} val
@@ -7028,6 +7196,7 @@
7028
7196
  let html = raw;
7029
7197
  if (isWordContent) html = this._cleanWordHtml(html);
7030
7198
  else if (isSocialContent) html = this._cleanSocialHtml(html);
7199
+ html = this._normalizeExternalTaskLists(html);
7031
7200
  html = sanitiseHTML(html);
7032
7201
  if (this.options.pasteStripAttributes) html = this._stripAttributes(html);
7033
7202
  execCommand("insertHTML", html);
@@ -16971,6 +17140,14 @@
16971
17140
  }
16972
17141
  };
16973
17142
  //#endregion
17143
+ //#region src/js/core/env.js
17144
+ /**
17145
+ * env.js - Environment / browser detection
17146
+ * Inspired by Summernote's env.js
17147
+ */
17148
+ var userAgent = navigator.userAgent;
17149
+ /Chrome\//.test(userAgent), /Firefox\//.test(userAgent), /^((?!chrome|android).)*safari/i.test(userAgent), /Edg\//.test(userAgent), /Macintosh/.test(userAgent), /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent), "ontouchstart" in globalThis || navigator.maxTouchPoints, /Macintosh/.test(userAgent);
17150
+ //#endregion
16974
17151
  //#region src/js/index.js
16975
17152
  var _originalDefaults = { ...defaultOptions };
16976
17153
  /** @type {WeakMap<Element, Context>} */
@@ -17079,7 +17256,7 @@
17079
17256
  /** All pre-built button definitions — accessible in every module format including UMD/CJS. */
17080
17257
  buttons,
17081
17258
  /** Library version */
17082
- version: "1.8.3"
17259
+ version: "1.10.0"
17083
17260
  };
17084
17261
  /**
17085
17262
  * @param {string|Element|NodeList|Element[]} selector