autumnnote 1.9.1 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autumnnote",
3
- "version": "1.9.1",
3
+ "version": "1.10.0",
4
4
  "description": "WYSIWYG rich-text editor built with vanilla JavaScript — zero dependencies, no jQuery. Dark mode, @mention, markdown shortcuts, bubble toolbar. React and Vue 3 wrappers included.",
5
5
  "main": "dist/autumnnote.umd.js",
6
6
  "module": "dist/autumnnote.es.js",
@@ -141,7 +141,9 @@ function _domToMd(node, depth = 0) {
141
141
  export function isMarkdown(text) {
142
142
  return /^#{1,6} [^\s]|^[ \t]*[-*+] [^\s]|^[ \t]*\d+\. [^\s]|^> [^\s]|^```|^\*{2}[^*\n]+\*{2}/m.test(text)
143
143
  || /^.+\n=+\s*$/m.test(text)
144
- || /^.+\n-{2,}\s*$/m.test(text);
144
+ || /^.+\n-{2,}\s*$/m.test(text)
145
+ || /^---\s*\n(?:[\s\S]*?\n)?(?:---|\.\.\.)\s*(?:\n|$)/.test(text)
146
+ || /^\|.+\|[ \t]*\n\|[ \t:|-]+\|/m.test(text);
145
147
  }
146
148
 
147
149
  /**
@@ -150,7 +152,12 @@ export function isMarkdown(text) {
150
152
  * @returns {string}
151
153
  */
152
154
  export function markdownToHTML(text) {
153
- const lines = text.replaceAll('\r\n', '\n').replaceAll('\r', '\n').split('\n');
155
+ let lines = text.replaceAll('\r\n', '\n').replaceAll('\r', '\n').split('\n');
156
+ lines = _stripFrontmatter(lines);
157
+ const refs = _extractReferenceDefinitions(lines);
158
+ lines = refs.clean;
159
+ _linkDefs = refs.linkDefs;
160
+ _footnoteIds = refs.footnoteIds;
154
161
  const out = [];
155
162
  let i = 0;
156
163
 
@@ -286,6 +293,68 @@ export function markdownToHTML(text) {
286
293
  // Inline formatting
287
294
  // ---------------------------------------------------------------------------
288
295
 
296
+ /** Reference-link and footnote definitions collected per markdownToHTML() call. */
297
+ let _linkDefs = new Map();
298
+ let _footnoteIds = new Set();
299
+
300
+ /**
301
+ * Strips a leading YAML frontmatter block (--- ... --- or --- ... ...) from
302
+ * the line array, only when it is the very first line and the enclosed body
303
+ * looks like YAML (key: value / list items / indented continuations) — this
304
+ * disambiguates real frontmatter from a horizontal rule followed by prose.
305
+ * @param {string[]} lines
306
+ * @returns {string[]}
307
+ */
308
+ function _stripFrontmatter(lines) {
309
+ if ((lines[0] || '').trim() !== '---') return lines;
310
+ let closeIdx = -1;
311
+ for (let j = 1; j < lines.length; j++) {
312
+ const t = lines[j].trim();
313
+ if (t === '---' || t === '...') { closeIdx = j; break; }
314
+ }
315
+ if (closeIdx === -1) return lines;
316
+
317
+ const body = lines.slice(1, closeIdx);
318
+ const looksLikeYAML = body.every((l) =>
319
+ l.trim() === '' ||
320
+ /^[ \t]*[\w$.-]+\s*:(\s|$)/.test(l) ||
321
+ /^[ \t]*-\s+\S/.test(l) ||
322
+ /^[ \t]+\S/.test(l));
323
+ if (!looksLikeYAML) return lines;
324
+
325
+ let start = closeIdx + 1;
326
+ if (lines[start] !== undefined && lines[start].trim() === '') start++;
327
+ return lines.slice(start);
328
+ }
329
+
330
+ /**
331
+ * Extracts GFM reference-link definitions (`[ref]: url "title"`) and footnote
332
+ * definitions (`[^id]: text`) from the line array, skipping fenced code
333
+ * regions. Returns the definition-free line array plus lookup maps.
334
+ * @param {string[]} lines
335
+ * @returns {{ clean: string[], linkDefs: Map<string, {href: string, title?: string}>, footnoteIds: Set<string> }}
336
+ */
337
+ function _extractReferenceDefinitions(lines) {
338
+ const linkDefs = new Map();
339
+ const footnoteIds = new Set();
340
+ const clean = [];
341
+ let inFence = false;
342
+ const linkDefRe = /^\[([^\]]+)\]:\s*(\S+)(?:\s+"([^"]*)")?\s*$/;
343
+ const footnoteDefRe = /^\[\^([^\]]+)\]:\s*(.+)$/;
344
+
345
+ for (const line of lines) {
346
+ if (/^```/.test(line)) { inFence = !inFence; clean.push(line); continue; }
347
+ if (!inFence) {
348
+ const fm = footnoteDefRe.exec(line);
349
+ if (fm) { footnoteIds.add(fm[1]); continue; }
350
+ const lm = linkDefRe.exec(line);
351
+ if (lm) { linkDefs.set(lm[1].trim().toLowerCase(), { href: lm[2], title: lm[3] }); continue; }
352
+ }
353
+ clean.push(line);
354
+ }
355
+ return { clean, linkDefs, footnoteIds };
356
+ }
357
+
289
358
  /**
290
359
  * Splits a GFM table row string into trimmed cell strings.
291
360
  * '| a | b | c |' → ['a', 'b', 'c']
@@ -356,6 +425,22 @@ function _inline(text) {
356
425
  // Links
357
426
  text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, href) =>
358
427
  `<a href="${_escAttr(href)}">${_esc(label)}</a>`);
428
+ // Reference-style links [text][ref] and shortcut [text][]
429
+ text = text.replace(/\[([^\]]+)\]\[([^\]]*)\]/g, (m, label, ref) => {
430
+ const def = _linkDefs.get((ref || label).trim().toLowerCase());
431
+ if (!def) return m;
432
+ const titleAttr = def.title ? ` title="${_escAttr(def.title)}"` : '';
433
+ return `<a href="${_escAttr(def.href)}"${titleAttr}>${_esc(label)}</a>`;
434
+ });
435
+ // Bare/implicit reference link [text] — only when a definition exists
436
+ text = text.replace(/\[([^\]]+)\]/g, (m, label) => {
437
+ const def = _linkDefs.get(label.trim().toLowerCase());
438
+ if (!def) return m;
439
+ const titleAttr = def.title ? ` title="${_escAttr(def.title)}"` : '';
440
+ return `<a href="${_escAttr(def.href)}"${titleAttr}>${_esc(label)}</a>`;
441
+ });
442
+ // Footnote reference marker [^id] — run last
443
+ text = text.replace(/\[\^([^\]]+)\]/g, (m, id) => (_footnoteIds.has(id) ? `<sup>[${_esc(id)}]</sup>` : m));
359
444
  // Bold + italic ***text***
360
445
  text = text.replace(/\*{3}([^*\n]+?)\*{3}/g, (_, c) => `<strong><em>${_esc(c)}</em></strong>`);
361
446
  text = text.replace(/_{3}([^_\n]+?)_{3}/g, (_, c) => `<strong><em>${_esc(c)}</em></strong>`);
package/src/js/index.js CHANGED
@@ -144,7 +144,7 @@ const AutumnNote = {
144
144
  buttons,
145
145
 
146
146
  /** Library version */
147
- version: '1.9.1',
147
+ version: '1.10.0',
148
148
  };
149
149
 
150
150
  // ---------------------------------------------------------------------------
package/types/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * AutumnNote – TypeScript declarations
3
- * @version 1.9.1
3
+ * @version 1.10.0
4
4
  */
5
5
 
6
6
  // ---------------------------------------------------------------------------