build-container / build (push) Successful in 6m4s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
32 lines
2.8 KiB
JavaScript
32 lines
2.8 KiB
JavaScript
'use strict';
|
|
// ---- A small, safe Markdown renderer for notes: headings, bold, italic, strike, code, links, quotes, bullet and numbered lists, and "- [ ]" tick boxes.
|
|
// Everything is escaped first, and only http(s) links are made clickable, so a note can never inject a script or a strange link.
|
|
function ntMd(src) {
|
|
const inline = t => esc(t)
|
|
.replace(/`([^`]+)`/g, '<code>$1</code>')
|
|
.replace(/\*\*([^*]+)\*\*/g, '<b>$1</b>').replace(/(^|[\s(])\*([^*\s][^*]*)\*/g, '$1<i>$2</i>').replace(/~~([^~]+)~~/g, '<s>$1</s>')
|
|
.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>')
|
|
.replace(/(^|[\s(])(https?:\/\/[^\s<)]+)/g, '$1<a href="$2" target="_blank" rel="noopener noreferrer">$2</a>');
|
|
const lines = String(src || '').replace(/\r/g, '').split('\n'), out = []; let list = null, code = false, para = [];
|
|
const endPara = () => { if (para.length) { out.push('<p>' + para.map(inline).join('<br>') + '</p>'); para = []; } };
|
|
const endList = () => { if (list) { out.push(`</${list}>`); list = null; } };
|
|
lines.forEach((raw, i) => {
|
|
if (/^```/.test(raw)) { endPara(); endList(); out.push(code ? '</code></pre>' : '<pre><code>'); code = !code; return; }
|
|
if (code) { out.push(esc(raw) + '\n'); return; }
|
|
let m;
|
|
if ((m = /^(\s*)[-*+] \[( |x|X)\] (.*)$/.exec(raw))) { endPara(); if (list !== 'ul') { endList(); out.push('<ul class="ntchk">'); list = 'ul'; } out.push(`<li class="${m[2] === ' ' ? '' : 'done'}"><input type="checkbox" data-ln="${i}" ${m[2] === ' ' ? '' : 'checked'}><span>${inline(m[3])}</span></li>`); return; }
|
|
if ((m = /^\s*[-*+] (.*)$/.exec(raw))) { endPara(); if (list !== 'ul') { endList(); out.push('<ul>'); list = 'ul'; } out.push('<li>' + inline(m[1]) + '</li>'); return; }
|
|
if ((m = /^\s*\d+[.)] (.*)$/.exec(raw))) { endPara(); if (list !== 'ol') { endList(); out.push('<ol>'); list = 'ol'; } out.push('<li>' + inline(m[1]) + '</li>'); return; }
|
|
endList();
|
|
if ((m = /^(#{1,3}) (.*)$/.exec(raw))) { endPara(); out.push(`<h${m[1].length + 1}>${inline(m[2])}</h${m[1].length + 1}>`); return; }
|
|
if (/^\s*(---|\*\*\*)\s*$/.test(raw)) { endPara(); out.push('<hr>'); return; }
|
|
if ((m = /^> ?(.*)$/.exec(raw))) { endPara(); out.push('<blockquote>' + inline(m[1]) + '</blockquote>'); return; }
|
|
if (!raw.trim()) { endPara(); return; }
|
|
para.push(raw);
|
|
});
|
|
endPara(); endList(); if (code) out.push('</code></pre>');
|
|
return out.join('');
|
|
}
|
|
/** The text of a note without the Markdown marks, for the small preview on a card. */
|
|
const ntPlain = s => String(s || '').replace(/```/g, '').replace(/^#{1,3} /gm, '').replace(/^\s*[-*+] \[( |x|X)\] /gm, (m, x) => x === ' ' ? '☐ ' : '☑ ').replace(/^\s*[-*+] /gm, '• ').replace(/^> ?/gm, '').replace(/\*\*([^*]+)\*\*/g, '$1').replace(/~~([^~]+)~~/g, '$1').replace(/\[([^\]]+)\]\([^)]+\)/g, '$1').replace(/`([^`]+)`/g, '$1');
|