Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/ui/file-preview/src/markdown/linking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ interface ParsedWikiLink {

const WIKI_LINK_PATTERN = /\[\[([^\]|#]*)(?:#([^\]|]+))?(?:\|([^\]]+))?\]\]/g;
const FENCE_PATTERN = /^(`{3,}|~{3,})/;
const FRONTMATTER_DELIMITER_PATTERN = /^---\s*$/;

function isFrontmatterDelimiter(line: string | undefined): boolean {
return typeof line === 'string' && FRONTMATTER_DELIMITER_PATTERN.test(line);
}

function encodeLinkPath(pathValue: string): string {
return encodeURI(normalizePathSeparators(pathValue));
Expand Down Expand Up @@ -218,8 +223,21 @@ export function restoreWikiLinks(markdown: string): string {
export function rewriteWikiLinks(source: string): string {
const lines = source.split('\n');
let activeFence: string | null = null;
let inFrontmatter = isFrontmatterDelimiter(lines[0])
&& lines.slice(1).some((line) => isFrontmatterDelimiter(line));

return lines.map((line, index) => {
if (index === 0 && inFrontmatter) {
return line;
}

if (inFrontmatter) {
if (isFrontmatterDelimiter(line)) {
inFrontmatter = false;
}
return line;
}

return lines.map((line) => {
const trimmedStart = line.trimStart();
const fenceMatch = trimmedStart.match(FENCE_PATTERN);
if (fenceMatch) {
Expand Down
48 changes: 48 additions & 0 deletions test/test-markdown-preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,54 @@ async function testWikiRewriteAndRendering() {
assert.ok(fencedRewrite.includes('[[Inside Code]]'), 'Long code fences should remain open until a matching-length close fence appears');
assert.ok(fencedRewrite.includes('[Outside Code](./Outside%20Code.md "mcp-wiki:'), 'Wiki links outside closed fences should still rewrite');

const frontmatterRewrite = rewriteWikiLinks([
'---',
'title: My Note',
'source_talk: "[[Other Note]]"',
'tags: [example]',
'---',
'',
'Body text with a [[Body Link]] for comparison.',
].join('\n'));
assert.strictEqual(
frontmatterRewrite,
[
'---',
'title: My Note',
'source_talk: "[[Other Note]]"',
'tags: [example]',
'---',
'',
'Body text with a [Body Link](./Body%20Link.md "mcp-wiki:%5B%5BBody%20Link%5D%5D") for comparison.',
].join('\n'),
'YAML frontmatter wikilinks should stay literal while body wikilinks still render',
);

const frontmatterBlockScalarRewrite = rewriteWikiLinks([
'---',
'title: |',
' intro text',
' ---',
'source_talk: "[[Frontmatter Link]]"',
'---',
'',
'Body text with a [[Body Link]] for comparison.',
].join('\n'));
assert.strictEqual(
frontmatterBlockScalarRewrite,
[
'---',
'title: |',
' intro text',
' ---',
'source_talk: "[[Frontmatter Link]]"',
'---',
'',
'Body text with a [Body Link](./Body%20Link.md "mcp-wiki:%5B%5BBody%20Link%5D%5D") for comparison.',
].join('\n'),
'Indented --- inside YAML block scalars should not close frontmatter early',
);

const html = renderMarkdown([
'# Title',
'## Details',
Expand Down