mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-10-13 04:01:35 +00:00
fix(studio): preserve Markdown references and escaped wikilinks
This commit is contained in:
parent
7d49b700c0
commit
da7ee9ba12
2 changed files with 62 additions and 38 deletions
|
|
@ -114,40 +114,34 @@ export function remarkWikilinks(this: Processor) {
|
|||
const visit = (node: MarkdownNode) => {
|
||||
if (
|
||||
!node.children ||
|
||||
["link", "image", "code", "inlineCode"].includes(node.type)
|
||||
[
|
||||
"link",
|
||||
"linkReference",
|
||||
"image",
|
||||
"imageReference",
|
||||
"code",
|
||||
"inlineCode",
|
||||
].includes(node.type)
|
||||
) {
|
||||
literalize(node);
|
||||
return;
|
||||
}
|
||||
node.children = node.children.flatMap((child) => {
|
||||
if (
|
||||
(child.type !== "text" && child.type !== "remeWikilink") ||
|
||||
!child.value
|
||||
) {
|
||||
node.children = node.children.map((child) => {
|
||||
if (child.type !== "remeWikilink" || !child.value) {
|
||||
visit(child);
|
||||
return [child];
|
||||
return child;
|
||||
}
|
||||
const parts: MarkdownNode[] = [];
|
||||
let end = 0;
|
||||
for (const match of child.value.matchAll(
|
||||
/\[\[([^[\]|#\n]+?)(?:#[^[\]|\n]+)?(?:\|([^[\]\n]+))?\]\]/g,
|
||||
)) {
|
||||
const target = match[1].trim();
|
||||
if (!target) continue;
|
||||
parts.push({
|
||||
type: "text",
|
||||
value: child.value.slice(end, match.index),
|
||||
});
|
||||
parts.push({
|
||||
type: "link",
|
||||
url: `#reme-file=${encodeURIComponent(target)}`,
|
||||
children: [{ type: "text", value: match[2]?.trim() || target }],
|
||||
});
|
||||
end = match.index + match[0].length;
|
||||
const match = WIKILINK_PATTERN.exec(child.value);
|
||||
if (!match?.[1].trim()) {
|
||||
literalize(child);
|
||||
return child;
|
||||
}
|
||||
if (!end) return [child];
|
||||
parts.push({ type: "text", value: child.value.slice(end) });
|
||||
return parts;
|
||||
const target = match[1].trim();
|
||||
return {
|
||||
type: "link",
|
||||
url: `#reme-file=${encodeURIComponent(target)}`,
|
||||
children: [{ type: "text", value: match[2]?.trim() || target }],
|
||||
};
|
||||
});
|
||||
};
|
||||
visit(tree);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,16 @@ function parse(content, wikilinks = true) {
|
|||
return processor.runSync(processor.parse(content));
|
||||
}
|
||||
|
||||
function render(content, wikilinks = true) {
|
||||
return renderToStaticMarkup(
|
||||
createElement(
|
||||
ReactMarkdown,
|
||||
{ remarkPlugins: wikilinks ? [remarkGfm, remarkWikilinks] : [remarkGfm] },
|
||||
content,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function links(tree) {
|
||||
return (
|
||||
tree.children?.flatMap((node) =>
|
||||
|
|
@ -72,17 +82,7 @@ test("code examples and existing Markdown links are not rewritten", () => {
|
|||
"`[[sample.md]]` and [See [[sample.md]]](https://example.com)",
|
||||
"![See [[sample.md]]](image.png)",
|
||||
].join("\n");
|
||||
const render = (plugins) =>
|
||||
renderToStaticMarkup(
|
||||
createElement(
|
||||
ReactMarkdown,
|
||||
{
|
||||
remarkPlugins: plugins,
|
||||
},
|
||||
content,
|
||||
),
|
||||
);
|
||||
assert.equal(render([remarkGfm, remarkWikilinks]), render([remarkGfm]));
|
||||
assert.equal(render(content), render(content, false));
|
||||
});
|
||||
|
||||
test("malformed wikilinks leave normal Markdown parsing unchanged", () => {
|
||||
|
|
@ -107,3 +107,33 @@ test("wikilinks work beside ordinary emphasis and consecutive links", () => {
|
|||
assert.equal(tree.children[0].children[0].type, "strong");
|
||||
assert.equal(tree.children[0].children.at(-1).type, "emphasis");
|
||||
});
|
||||
|
||||
test("reference links and images preserve literal wikilinks in their labels", () => {
|
||||
for (const content of [
|
||||
"[See [[sample.md]]][ref]\n\n[ref]: https://example.com",
|
||||
"[See [[sample.md]]][REF]\n\n[ref]: https://example.com",
|
||||
"![See [[sample.md]]][ref]\n\n[ref]: image.png",
|
||||
]) {
|
||||
assert.equal(render(content), render(content, false));
|
||||
assert.doesNotMatch(render(content), /#reme-file=/);
|
||||
}
|
||||
});
|
||||
|
||||
test("escaped brackets and character entities remain literal text", () => {
|
||||
for (const content of [
|
||||
String.raw`\[\[example.md\]\]`,
|
||||
"[[example.md]]",
|
||||
"[[example.md]]",
|
||||
String.raw`\[[example.md]]`,
|
||||
]) {
|
||||
assert.equal(render(content), render(content, false));
|
||||
assert.equal(links(parse(content)).length, 0);
|
||||
}
|
||||
const mixed = String.raw`\[\[literal.md\]\] [[real.md]] [[entity.md]]`;
|
||||
assert.deepEqual(
|
||||
links(parse(mixed)).map((node) => node.url),
|
||||
["#reme-file=real.md"],
|
||||
);
|
||||
assert.match(render(mixed), /\[\[literal\.md\]\]/);
|
||||
assert.match(render(mixed), /\[\[entity\.md\]\]/);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue