From 9f4c3ed35ac44c9edfe1d7b37c038cffb58d89c5 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Fri, 17 Apr 2026 07:49:38 +0300 Subject: [PATCH] feat(markdown): basic support hash-based wikilinks --- .../type_widgets/code/Markdown.spec.ts | 6 +++++ .../widgets/type_widgets/code/Markdown.tsx | 5 +++- packages/commons/src/lib/markdown_renderer.ts | 23 +++++++++++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/apps/client/src/widgets/type_widgets/code/Markdown.spec.ts b/apps/client/src/widgets/type_widgets/code/Markdown.spec.ts index aeb1aa1f41..61cf206417 100644 --- a/apps/client/src/widgets/type_widgets/code/Markdown.spec.ts +++ b/apps/client/src/widgets/type_widgets/code/Markdown.spec.ts @@ -79,4 +79,10 @@ describe("renderWithSourceLines", () => { const html = renderWithSourceLines("Energy: $e=mc^2$."); expect(html).toContain(''); }); + + it("renders [[wikilinks]] with hash-router hrefs so the preview navigates correctly", () => { + const html = renderWithSourceLines("See [[abc123]] for details."); + expect(html).toContain('class="reference-link"'); + expect(html).toContain('href="#root/abc123"'); + }); }); diff --git a/apps/client/src/widgets/type_widgets/code/Markdown.tsx b/apps/client/src/widgets/type_widgets/code/Markdown.tsx index 66c9fa0c90..ace105e7a5 100644 --- a/apps/client/src/widgets/type_widgets/code/Markdown.tsx +++ b/apps/client/src/widgets/type_widgets/code/Markdown.tsx @@ -149,7 +149,10 @@ export function renderWithSourceLines(src: string): string { if (!NON_RENDERED_TOKENS.has(token.type)) lines.push(startLine); } - const html = renderToHtml(src, "", { sanitize: (h) => DOMPurify.sanitize(h) }); + const html = renderToHtml(src, "", { + sanitize: (h) => DOMPurify.sanitize(h), + wikiLink: { formatHref: (id) => `#root/${id}` } + }); if (!html) return ""; const container = document.createElement("div"); diff --git a/packages/commons/src/lib/markdown_renderer.ts b/packages/commons/src/lib/markdown_renderer.ts index 865880e190..453c39d420 100644 --- a/packages/commons/src/lib/markdown_renderer.ts +++ b/packages/commons/src/lib/markdown_renderer.ts @@ -1,7 +1,14 @@ import { Marked, Renderer, type Tokens } from "marked"; import { getMimeTypeFromMarkdownName, MIME_TYPE_AUTO, normalizeMimeTypeForCKEditor } from "./mime_type.js"; -import { transclusionExtension, wikiLinkExtension } from "./marked_extensions.js"; +import { + createTransclusionExtension, + createWikiLinkExtension, + transclusionExtension, + type TransclusionOptions, + wikiLinkExtension, + type WikiLinkOptions +} from "./marked_extensions.js"; /** * Mapping from markdown admonition keywords (case-insensitive) to the ids @@ -24,6 +31,15 @@ export interface RenderToHtmlOptions { * - client: `DOMPurify.sanitize` */ sanitize: (dirtyHtml: string) => string; + /** + * How `[[noteId]]` wiki-links should be rendered. Defaults to the + * server-side format (`href="/noteId"`), which is what imports want. + * Browser callers that navigate via the hash router should pass + * `{ formatHref: (id) => `#root/${id}` }`. + */ + wikiLink?: WikiLinkOptions; + /** Same as {@link wikiLink}, for `![[noteId]]` transclusions. */ + transclusion?: TransclusionOptions; } function escapeHtml(str: string): string { @@ -253,7 +269,10 @@ export function renderToHtml(content: string, title: string, options: RenderToHt const marked = new Marked({ async: false }); marked.use({ // Order is important, especially for wikilinks. - extensions: [ transclusionExtension, wikiLinkExtension ] + extensions: [ + options.transclusion ? createTransclusionExtension(options.transclusion) : transclusionExtension, + options.wikiLink ? createWikiLinkExtension(options.wikiLink) : wikiLinkExtension + ] }); const renderer = new CustomMarkdownRenderer({ async: false });