fix(hololake): ship dynamic persona routes and coherent workspace

This commit is contained in:
冰朔 2026-08-09 10:43:56 +08:00
commit 787ba2ad3f
21 changed files with 1111 additions and 326 deletions

View file

@ -0,0 +1,64 @@
import { marked, Renderer } from 'marked';
const WIKI_LINK = /\[\[([^\]|\n]+?)(?:\|([^\]\n]+?))?\]\]/g;
const UNSAFE_SCHEME = /^(?:javascript|data|vbscript):/i;
export function normalizeImportedMarkup(source: string): string {
return source.replace(/<aside>\s*([\s\S]*?)\s*<\/aside>/giu, (_match, rawBody: string) => {
const body = rawBody.trim().split('\n').map(line => `> ${line}`).join('\n');
return `\n\n${body}\n\n`;
});
}
export function expandWikiLinks(source: string): string {
return source.replace(WIKI_LINK, (_match, rawTarget: string, rawLabel?: string) => {
const target = rawTarget.trim();
const label = (rawLabel || rawTarget).trim();
const encoded = encodeURIComponent(target).replace(/%2F/gi, '/');
return `[${label}](hololake://knowledge/${encoded} "${target.replace(/"/g, '&quot;')}")`;
});
}
function escapeHtml(value: string): string {
return value
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function safeHref(href: string | null): string {
const value = (href || '').trim();
return UNSAFE_SCHEME.test(value) ? '#' : value;
}
const renderer = new Renderer();
const renderBaseTable = renderer.table.bind(renderer);
renderer.html = ({ text }) => /^<br\s*\/?\s*>$/iu.test(text.trim())
? '<br>'
: `<pre class="kb-raw-html"><code>${escapeHtml(text)}</code></pre>`;
renderer.link = ({ href, title, tokens }) => {
const label = renderer.parser.parseInline(tokens);
const safe = escapeHtml(safeHref(href));
const titleAttr = title ? ` title="${escapeHtml(title)}"` : '';
const wiki = safe.startsWith('hololake://knowledge/');
return `<a href="${safe}"${titleAttr}${wiki ? ' data-hololake-wiki="true"' : ''}>${label}</a>`;
};
renderer.image = ({ href, title, text }) => {
const safe = safeHref(href);
if (!safe || safe === '#') return `<span class="kb-image-blocked">图片地址已拦截</span>`;
const titleAttr = title ? ` title="${escapeHtml(title)}"` : '';
return `<img src="${escapeHtml(safe)}" alt="${escapeHtml(text)}"${titleAttr} loading="lazy">`;
};
renderer.table = token => `<div class="kb-table-scroll">${renderBaseTable(token)}</div>`;
marked.setOptions({ gfm: true, breaks: false });
export function renderKnowledgeMarkdown(source: string): string {
try {
return marked.parse(expandWikiLinks(normalizeImportedMarkup(source)), { async: false, renderer }) as string;
} catch {
return '<p class="kb-render-error">此页面暂时无法渲染,请切换到编辑模式检查原文。</p>';
}
}