From b12e0c97b70310230e01e2f4262726f43581a9cf Mon Sep 17 00:00:00 2001 From: Matt Vogel Date: Wed, 6 Mar 2024 22:58:33 -0500 Subject: [PATCH] feat: Roam Research flavor markdown --- quartz.config.ts | 1 + quartz/plugins/transformers/index.ts | 1 + quartz/plugins/transformers/roam.ts | 132 +++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 quartz/plugins/transformers/roam.ts diff --git a/quartz.config.ts b/quartz.config.ts index 2cdadb740..80030f0a3 100644 --- a/quartz.config.ts +++ b/quartz.config.ts @@ -63,6 +63,7 @@ const config: QuartzConfig = { }, keepBackground: false, }), + Plugin.RoamFlavoredMarkdown(), Plugin.ObsidianFlavoredMarkdown({ enableInHtmlEmbed: false }), Plugin.GitHubFlavoredMarkdown(), Plugin.TableOfContents(), diff --git a/quartz/plugins/transformers/index.ts b/quartz/plugins/transformers/index.ts index e340f10e7..51232d423 100644 --- a/quartz/plugins/transformers/index.ts +++ b/quartz/plugins/transformers/index.ts @@ -9,3 +9,4 @@ export { OxHugoFlavouredMarkdown } from "./oxhugofm" export { SyntaxHighlighting } from "./syntax" export { TableOfContents } from "./toc" export { HardLineBreaks } from "./linebreaks" +export { RoamFlavoredMarkdown } from "./roam" diff --git a/quartz/plugins/transformers/roam.ts b/quartz/plugins/transformers/roam.ts new file mode 100644 index 000000000..130158532 --- /dev/null +++ b/quartz/plugins/transformers/roam.ts @@ -0,0 +1,132 @@ +import { QuartzTransformerPlugin } from "../types" + +export interface Options { + orComponent: boolean + TODOComponent: boolean + DONEComponent: boolean + videoComponent: boolean + audioComponent: boolean + pdfComponent: boolean + blockquoteComponent: boolean + tableComponent: boolean + attributeComponent: boolean +} + +const defaultOptions: Options = { + orComponent: true, + TODOComponent: true, + DONEComponent: true, + videoComponent: true, + audioComponent: true, + pdfComponent: true, + blockquoteComponent: true, + tableComponent: true, + attributeComponent: true, +} + +const orRegex = new RegExp(/{{or:(.*?)}}/, "g") +const TODORegex = new RegExp(/{{.*?\bTODO\b.*?}}/, "g") +const DONERegex = new RegExp(/{{.*?\bDONE\b.*?}}/, "g") +const videoRegex = new RegExp(/{{.*?\bvideo\b.*?\:(.*?)}}/, "g") +const youtubeRegex = new RegExp(/{{.*?\bvideo\b.*?(\bhttp.*?\byoutu.*?)watch\?v=(.*?)}}/, "g") + +const audioRegex = new RegExp(/{{.*?\baudio\b.*?\:(.*?)}}/, "g") +const pdfRegex = new RegExp(/{{.*?\bpdf\b.*?\:(.*?)}}/, "g") +const blockquoteRegex = new RegExp(/\[\[>\]\]/, "g") +const roamHighlightRegex = new RegExp(/\^\^(.+)\^\^/, "g") +const roamItalicRegex = new RegExp(/__(.+)__/, "g") +const tableRegex = new RegExp(/- {{.*?\btable\b.*?}}/, "g") /* TODO */ +const attributeRegex = new RegExp(/\b\w+(?:\s+\w+)*::/, "g") /* TODO */ + +export const RoamFlavoredMarkdown: QuartzTransformerPlugin | undefined> = ( + userOpts, +) => { + const opts = { ...defaultOptions, ...userOpts } + + return { + name: "RoamFlavoredMarkdown", + textTransform(_ctx, src) { + if (opts.orComponent) { + src = src.toString() + src = src.replaceAll(orRegex, (value, ...capture) => { + const [match, text] = capture + const options = match.split("|") + const id = Math.random().toString(36).substring(2, 15) // Generate a unique ID + const dropdown = `` + return dropdown + }) + } + if (opts.TODOComponent) { + src = src.toString() + src = src.replaceAll(TODORegex, (value, ...capture) => { + const checkbox = '' + return checkbox + }) + } + if (opts.DONEComponent) { + src = src.toString() + src = src.replaceAll(DONERegex, (value, ...capture) => { + const checkbox = '' + return checkbox + }) + } + if (opts.videoComponent) { + //youtube first then regular video links + src = src.toString() + src = src.replaceAll(youtubeRegex, (value, ...capture) => { + const [match, text] = capture + const video = `` + return video + }) + src = src.replaceAll(videoRegex, (value, ...capture) => { + const [match, text] = capture + const video = `` + return video + }) + } + if (opts.audioComponent) { + src = src.toString() + src = src.replaceAll(audioRegex, (value, ...capture) => { + const [match, text] = capture + const audio = `` + return audio + }) + } + if (opts.pdfComponent) { + src = src.toString() + src = src.replaceAll(pdfRegex, (value, ...capture) => { + const [match, text] = capture + const pdf = `` + return pdf + }) + } + if (opts.blockquoteComponent) { + src = src.toString() + src = src.replaceAll(blockquoteRegex, (value, ...capture) => { + const bq = `>` + return bq + }) + } + // TODO attributes in roam are sort of like block level frontmatter or tags + + src = src.toString() + src = src.replaceAll(roamHighlightRegex, (value, ...capture) => { + const [match, text] = capture + const highlight = `==${match}==` + return highlight + }) + // roam italics + src = src.replaceAll(roamItalicRegex, (value, ...capture) => { + const [match, text] = capture + const italic = `*${match}*` + return italic + }) + + return src + }, + } +}