diff --git a/docs/plugins/ObsidianFlavoredMarkdown.md b/docs/plugins/ObsidianFlavoredMarkdown.md index 414f743b8..6de9cac18 100644 --- a/docs/plugins/ObsidianFlavoredMarkdown.md +++ b/docs/plugins/ObsidianFlavoredMarkdown.md @@ -23,6 +23,7 @@ This plugin accepts the following configuration options: - `enableYouTubeEmbed`: If `true` (default), enables the embedding of YouTube videos and playlists using external image Markdown syntax. - `enableVideoEmbed`: If `true` (default), enables the embedding of video files. - `enableCheckbox`: If `true`, adds support for interactive checkboxes in content. Defaults to `false`. +- `enableInlineFootnotes`: If `true` (default), enables parsing of inline footnotes. > [!warning] > Don't remove this plugin if you're using [[Obsidian compatibility|Obsidian]] to author the content! diff --git a/quartz/plugins/transformers/ofm.ts b/quartz/plugins/transformers/ofm.ts index ef5917926..96ae43448 100644 --- a/quartz/plugins/transformers/ofm.ts +++ b/quartz/plugins/transformers/ofm.ts @@ -41,6 +41,7 @@ export interface Options { enableYouTubeEmbed: boolean enableVideoEmbed: boolean enableCheckbox: boolean + enableInlineFootnotes: boolean } const defaultOptions: Options = { @@ -56,6 +57,7 @@ const defaultOptions: Options = { enableYouTubeEmbed: true, enableVideoEmbed: true, enableCheckbox: false, + enableInlineFootnotes: true, } const calloutMapping = { @@ -146,6 +148,13 @@ const wikilinkImageEmbedRegex = new RegExp( /^(?(?!^\d*x?\d*$).*?)?(\|?\s*?(?\d+)(x(?\d+))?)?$/, ) +const inlineFootnoteRegex = new RegExp(/\^\[((?:[^\[\]]|\[(?:[^\[\]]|\[[^\[\]]*\])*\])*)\]/g) +// match inline footnotes where content can contain any properly nested brackets +// \^\[...\] -> matches ^[inline footnote's] brackets +// (?:...) -> does not capture any of the following: +// [^\[\]] -> any character that is not a bracket +// \[(?:[^\[\]]|\[[^\[\]]*\])*\] -> a properly nested set of brackets + export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin> = (userOpts) => { const opts = { ...defaultOptions, ...userOpts } @@ -204,6 +213,36 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin> }) } + if (opts.enableInlineFootnotes) { + // Replaces ^[inline] footnotes with regular footnotes [^1]: + const footnotes: Record = {} + let counter = 0 + + // Replace inline footnotes with references and collect definitions + const result = (src as string).replace( + inlineFootnoteRegex, + (_match: string, content: string) => { + const id = `generated-inline-footnote-${counter++}` + footnotes[id] = content.trim() + return `[^${id}]` + }, + ) + + // Append footnote definitions if any are found + if (Object.keys(footnotes).length > 0) { + return ( + result + + "\n\n" + + Object.entries(footnotes) + .map(([id, content]) => `[^${id}]: ${content}`) + .join("\n") + + "\n" + ) + } + + return result + } + return src }, markdownPlugins(_ctx) {