136 lines
4.9 KiB
JavaScript
136 lines
4.9 KiB
JavaScript
export function createSidebarPageAiMarkdownRuntime(context) {
|
|
const { escapeHtml } = context;
|
|
|
|
function textFromUnknown(value) {
|
|
if (value == null) return '';
|
|
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
if (Array.isArray(value)) return value.map(textFromUnknown).filter(Boolean).join(' ');
|
|
if (typeof value !== 'object') return '';
|
|
var parts = [];
|
|
['text', 'title', 'content', 'children', 'blocks', 'body'].forEach(function(key) {
|
|
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
var text = textFromUnknown(value[key]);
|
|
if (text) parts.push(text);
|
|
}
|
|
});
|
|
return parts.join(' ');
|
|
}
|
|
|
|
function renderPageAiMarkdownInline(text) {
|
|
var html = escapeHtml(String(text || ''));
|
|
var codeSpans = [];
|
|
html = html.replace(/`([^`\n]+)`/g, function(_, code) {
|
|
var key = '\u0000CODE' + codeSpans.length + '\u0000';
|
|
codeSpans.push('<code>' + code + '</code>');
|
|
return key;
|
|
});
|
|
html = html.replace(/\[([^\]\n]+)\]\(([^)\s]+)\)/g, function(match, label, href) {
|
|
var normalizedHref = normalizePageAiMarkdownHref(href);
|
|
if (!normalizedHref) return match;
|
|
var citationAttr = isMnoteCitationHref(normalizedHref) ? ' data-page-ai-citation-link="true"' : '';
|
|
return '<a href="' + escapeHtml(normalizedHref) + '" target="_blank" rel="noopener noreferrer"' + citationAttr + '>' + label + '</a>';
|
|
});
|
|
html = html.replace(/\*\*([^*\n]+)\*\*/g, '<strong>$1</strong>');
|
|
html = html.replace(/(^|[^*])\*([^*\n]+)\*/g, '$1<em>$2</em>');
|
|
codeSpans.forEach(function(value, index) {
|
|
html = html.replace('\u0000CODE' + index + '\u0000', value);
|
|
});
|
|
return html;
|
|
}
|
|
|
|
function normalizePageAiMarkdownHref(value) {
|
|
var href = String(value || '').trim()
|
|
.replace(/&/g, '&')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, "'");
|
|
if (!href || /[\u0000-\u001f<>"']/.test(href)) return '';
|
|
var lower = href.toLowerCase();
|
|
if (lower.startsWith('javascript:') || lower.startsWith('data:') || lower.startsWith('vbscript:')) return '';
|
|
if (href.startsWith('/') || href.startsWith('#')) return href;
|
|
if (/^https?:\/\//i.test(href) || /^mailto:/i.test(href)) return href;
|
|
return '';
|
|
}
|
|
|
|
function isMnoteCitationHref(href) {
|
|
try {
|
|
var url = new URL(href, window.location.origin);
|
|
return url.origin === window.location.origin && url.pathname.startsWith('/documents/');
|
|
} catch (_error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function renderPageAiMarkdown(content) {
|
|
var lines = String(content || '').replace(/\r\n?/g, '\n').split('\n');
|
|
var blocks = [];
|
|
var index = 0;
|
|
function isBlockBoundary(line) {
|
|
return !line.trim() ||
|
|
/^```/.test(line.trim()) ||
|
|
/^#{1,6}\s+/.test(line) ||
|
|
/^\s*[-*]\s+/.test(line) ||
|
|
/^\s*\d+[.)]\s+/.test(line);
|
|
}
|
|
while (index < lines.length) {
|
|
var line = lines[index];
|
|
if (!line.trim()) {
|
|
index += 1;
|
|
continue;
|
|
}
|
|
if (/^```/.test(line.trim())) {
|
|
index += 1;
|
|
var codeLines = [];
|
|
while (index < lines.length && !/^```/.test(lines[index].trim())) {
|
|
codeLines.push(lines[index]);
|
|
index += 1;
|
|
}
|
|
if (index < lines.length) index += 1;
|
|
blocks.push('<pre><code>' + escapeHtml(codeLines.join('\n')) + '</code></pre>');
|
|
continue;
|
|
}
|
|
var heading = line.match(/^(#{1,6})\s+(.+)$/);
|
|
if (heading) {
|
|
var level = Math.min(6, heading[1].length);
|
|
blocks.push('<h' + level + '>' + renderPageAiMarkdownInline(heading[2]) + '</h' + level + '>');
|
|
index += 1;
|
|
continue;
|
|
}
|
|
if (/^\s*[-*]\s+/.test(line)) {
|
|
var unordered = [];
|
|
while (index < lines.length && /^\s*[-*]\s+/.test(lines[index])) {
|
|
unordered.push('<li>' + renderPageAiMarkdownInline(lines[index].replace(/^\s*[-*]\s+/, '')) + '</li>');
|
|
index += 1;
|
|
}
|
|
blocks.push('<ul>' + unordered.join('') + '</ul>');
|
|
continue;
|
|
}
|
|
if (/^\s*\d+[.)]\s+/.test(line)) {
|
|
var ordered = [];
|
|
while (index < lines.length && /^\s*\d+[.)]\s+/.test(lines[index])) {
|
|
ordered.push('<li>' + renderPageAiMarkdownInline(lines[index].replace(/^\s*\d+[.)]\s+/, '')) + '</li>');
|
|
index += 1;
|
|
}
|
|
blocks.push('<ol>' + ordered.join('') + '</ol>');
|
|
continue;
|
|
}
|
|
var paragraph = [];
|
|
while (index < lines.length && !isBlockBoundary(lines[index])) {
|
|
paragraph.push(renderPageAiMarkdownInline(lines[index]));
|
|
index += 1;
|
|
}
|
|
if (paragraph.length) {
|
|
blocks.push('<p>' + paragraph.join('<br />') + '</p>');
|
|
} else {
|
|
index += 1;
|
|
}
|
|
}
|
|
return blocks.join('') || escapeHtml(String(content || ''));
|
|
}
|
|
|
|
return {
|
|
textFromUnknown,
|
|
renderPageAiMarkdown,
|
|
renderPageAiMarkdownInline
|
|
};
|
|
}
|