bak
This commit is contained in:
1 parent
03f57cbaee
commit
a9603aa0e8
11 files changed
+1010
-93
No files matched your search
@@ -11,14 +11,74 @@ const purifyConfig = {
|
||||
ALLOW_DATA_ATTR: true,
|
||||
};
|
||||
|
||||
function normalizeListBlock(block) {
|
||||
const lines = String(block || '').replace(/\r\n?/g, '\n').split('\n');
|
||||
while (lines.length && !lines[0].trim()) lines.shift();
|
||||
while (lines.length && !lines[lines.length - 1].trim()) lines.pop();
|
||||
if (!lines.length) return '';
|
||||
|
||||
const minIndent = lines
|
||||
.filter(l => l.trim())
|
||||
.map(l => (l.match(/^[ \t]*/) || [''])[0].replace(/\t/g, ' ').length)
|
||||
.reduce((min, n) => Math.min(min, n), Infinity);
|
||||
|
||||
return lines
|
||||
.map(l => {
|
||||
const expanded = l.replace(/\t/g, ' ');
|
||||
if (!expanded.trim()) return '';
|
||||
return expanded.slice(Math.min(minIndent, expanded.length));
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
export function renderMd(src) {
|
||||
const linked = (src || '').replace(/\[\[([^\]]+)\]\]/g, (_, title) => {
|
||||
let processed = String(src || '').replace(/\r\n?/g, '\n')
|
||||
// Custom fenced list blocks: ```tree / ```bullets with or without closing ```
|
||||
.replace(/```(tree|bullets)\s*\n([\s\S]*?)(?:```|$)/g, (_, kind, content) => {
|
||||
const normalized = normalizeListBlock(content);
|
||||
return `\n<!-- list:${kind} -->\n${normalized}\n`;
|
||||
})
|
||||
// Help parser: ensure a list can start after text without requiring manual blank line
|
||||
.replace(/([^\n])\n(?=(?: {0,3}[-*+]\s| {0,3}\d+\.\s))/g, '$1\n\n')
|
||||
// Preserve extra blank lines as <br> spacers (3+ newlines = 1+ extra blank lines)
|
||||
.replace(/\n{3,}/g, m => '\n\n' + '<br>\n'.repeat(m.length - 2))
|
||||
// Ensure hr rules have one clean blank line before and after, keep extra <br> beyond that
|
||||
.replace(/<br>\n(---+|___+|\*\*\*+)\n/gm, '\n\n$1\n')
|
||||
.replace(/(---+|___+|\*\*\*+)\n<br>/gm, '$1\n\n');
|
||||
|
||||
const linked = processed.replace(/\[\[([^\]]+)\]\]/g, (_, title) => {
|
||||
const card = state.cards.find(c => c.title.toLowerCase() === title.toLowerCase());
|
||||
if (card) return `<span class="card-link" data-card-id="${card.id}">${esc(title)}</span>`;
|
||||
return `<span class="card-link broken" title="card not found">${esc(title)}</span>`;
|
||||
});
|
||||
const raw = marked.parse(linked);
|
||||
return typeof DOMPurify !== 'undefined' ? DOMPurify.sanitize(raw, purifyConfig) : raw;
|
||||
|
||||
let html = marked.parse(linked);
|
||||
|
||||
// Convert list hints before sanitize (DOMPurify strips comments)
|
||||
html = html.replace(/<!--\s*list:tree\s*-->\s*<ul>/g, '<ul class="tree-list">');
|
||||
html = html.replace(/<!--\s*list:bullets\s*-->\s*<ul>/g, '<ul class="bullet-list">');
|
||||
|
||||
html = typeof DOMPurify !== 'undefined' ? DOMPurify.sanitize(html, purifyConfig) : html;
|
||||
return html;
|
||||
}
|
||||
|
||||
export function addCodeCopyButtons(container) {
|
||||
if (!container) return;
|
||||
container.querySelectorAll('pre > code').forEach(code => {
|
||||
const pre = code.parentElement;
|
||||
if (pre.querySelector('.code-copy')) return;
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'code-copy';
|
||||
btn.type = 'button';
|
||||
btn.innerHTML = '<i class="ph ph-copy"></i>';
|
||||
btn.addEventListener('click', async () => {
|
||||
await navigator.clipboard.writeText(code.textContent);
|
||||
btn.classList.add('copied');
|
||||
btn.innerHTML = '<i class="ph ph-check"></i>';
|
||||
setTimeout(() => { btn.classList.remove('copied'); btn.innerHTML = '<i class="ph ph-copy"></i>'; }, 1500);
|
||||
});
|
||||
pre.appendChild(btn);
|
||||
});
|
||||
}
|
||||
|
||||
export function stripMd(src) {
|
||||
|
||||
Reference in new issue
Block a user