Files
mnote/rust/crates/mnote-web/browser/sidebar-page-ai-markdown-runtime.js
T
lix-2026 1882db7681 收口 MNote P0 P1 P2 审查尾项
- 归档 OnlyOffice live bridge、Page AI、mindmap、design governance 与相关 bug 条目
- 补齐 MinerU OCR 后端 runtime 合同与 smoke/test 基线
- 收口 ChatOnly/Doubao、ObjectIdentity、Page Aggregate compat 与 runtime owner 文档口径

验证:
- cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1
- cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_bridge -- --test-threads=1
- git diff --check
- git diff --cached --check
- codegraph index . --force && codegraph status .
- codegraph sync . && codegraph status .
2026-06-01 09:29:12 +08:00

108 lines
3.7 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]+)\*\*/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 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
};
}