autumnnote 1.8.3 → 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.
@@ -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);
5313
5313
  }
5314
5314
  /**
5315
5315
  * Converts a Markdown string to an HTML string.
@@ -5336,6 +5336,18 @@
5336
5336
  i++;
5337
5337
  continue;
5338
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
+ }
5339
5351
  if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(line)) {
5340
5352
  out.push("<hr>");
5341
5353
  i++;
@@ -5358,30 +5370,15 @@
5358
5370
  continue;
5359
5371
  }
5360
5372
  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]}>`);
5373
+ const { html: listHtml, endIdx } = _parseListBlock(lines, i);
5374
+ out.push(listHtml);
5375
+ i = endIdx;
5376
5376
  continue;
5377
5377
  }
5378
5378
  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>`);
5379
+ const { html: listHtml, endIdx } = _parseListBlock(lines, i);
5380
+ out.push(listHtml);
5381
+ i = endIdx;
5385
5382
  continue;
5386
5383
  }
5387
5384
  if (line.trim() === "") {
@@ -5390,20 +5387,29 @@
5390
5387
  }
5391
5388
  if (/^\|.+\|/.test(line) && i + 1 < lines.length && /^\|[\s|:-]+\|/.test(lines[i + 1])) {
5392
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
+ });
5393
5396
  i += 2;
5394
5397
  const bodyRows = [];
5395
5398
  while (i < lines.length && /^\|.+\|/.test(lines[i])) {
5396
5399
  bodyRows.push(_parseTableRow(lines[i]));
5397
5400
  i++;
5398
5401
  }
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>`;
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>`;
5401
5407
  const tbody = bodyRows.length ? `<tbody>${bodyRows.map(renderRow).join("")}</tbody>` : "";
5402
5408
  out.push(`<table>${thead}${tbody}</table>`);
5403
5409
  continue;
5404
5410
  }
5405
5411
  const paraLines = [];
5406
- 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]))) {
5407
5413
  paraLines.push(lines[i]);
5408
5414
  i++;
5409
5415
  }
@@ -5420,6 +5426,57 @@
5420
5426
  function _parseTableRow(row) {
5421
5427
  return row.replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
5422
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
+ }
5423
5480
  function _inline(text) {
5424
5481
  text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, src) => `<img src="${_escAttr(src)}" alt="${_escAttr(alt)}" class="an-image">`);
5425
5482
  text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, href) => `<a href="${_escAttr(href)}">${_esc(label)}</a>`);
@@ -6971,6 +7028,29 @@
6971
7028
  return doc.body.innerHTML;
6972
7029
  }
6973
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
+ /**
6974
7054
  * Forces the next paste operation to strip all HTML formatting.
6975
7055
  * Called by Editor when Ctrl+Shift+V is pressed.
6976
7056
  * @param {boolean} val
@@ -7028,6 +7108,7 @@
7028
7108
  let html = raw;
7029
7109
  if (isWordContent) html = this._cleanWordHtml(html);
7030
7110
  else if (isSocialContent) html = this._cleanSocialHtml(html);
7111
+ html = this._normalizeExternalTaskLists(html);
7031
7112
  html = sanitiseHTML(html);
7032
7113
  if (this.options.pasteStripAttributes) html = this._stripAttributes(html);
7033
7114
  execCommand("insertHTML", html);
@@ -16971,6 +17052,14 @@
16971
17052
  }
16972
17053
  };
16973
17054
  //#endregion
17055
+ //#region src/js/core/env.js
17056
+ /**
17057
+ * env.js - Environment / browser detection
17058
+ * Inspired by Summernote's env.js
17059
+ */
17060
+ var userAgent = navigator.userAgent;
17061
+ /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);
17062
+ //#endregion
16974
17063
  //#region src/js/index.js
16975
17064
  var _originalDefaults = { ...defaultOptions };
16976
17065
  /** @type {WeakMap<Element, Context>} */
@@ -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.8.3"
17171
+ version: "1.9.1"
17083
17172
  };
17084
17173
  /**
17085
17174
  * @param {string|Element|NodeList|Element[]} selector