use markdownPlugins

This commit is contained in:
Matt Vogel 2024-03-15 19:29:29 -04:00
parent d9b1fb7d37
commit 4cc8a5a192
4 changed files with 205 additions and 73 deletions

43
content/roam-test.md Normal file
View File

@ -0,0 +1,43 @@
### OR
{{or:ONE|TWO|THREE}}
### Tasks
- {{[[TODO]]}} unchecked
- {{TODO}} unchecked
- {{[[DONE]]}} checked
- {{DONE}} checked
### Text Styling
- **Bold**
- __Italics__
- Highlights ^^of some^^ words
- this one, ~~not this one~~
- Blockquotes
- [[>]] Regulare quote using page brackets
- > bare quote without brackets
### Uploaded Files
- image
- ![](https://firebasestorage.googleapis.com/v0/b/firescript-577a2.appspot.com/o/imgs%2Fapp%2FMattVogel%2Ftiz3q_Bm-P.png?alt=media&token=ede018a1-ba7d-4099-8e6c-fa225550ef0e)
- video
- {{[[video]]: https://firebasestorage.googleapis.com/v0/b/firescript-577a2.appspot.com/o/imgs%2Fapp%2FMattVogel%2FBY1tSter6R.mp4?alt=media&token=07031095-4736-4b0b-899a-26852adbf245}}
- youtube
- {{[[video]]: https://www.youtube.com/watch?v=y1f6bfiSH94}}
- gif
- ![](https://firebasestorage.googleapis.com/v0/b/firescript-577a2.appspot.com/o/imgs%2Fapp%2FMattVogel%2FzAgByL_oF2.gif?alt=media&token=f754e4ba-aadb-43ad-82de-a2f79dc66370)
- pdf
- {{[[pdf]]: https://firebasestorage.googleapis.com/v0/b/firescript-577a2.appspot.com/o/imgs%2Fapp%2FMattVogel%2F6FtyOTB3mx.pdf?alt=media&token=0736266f-54d2-46dc-99a8-08f7e7874210}}
- audio
- This is being selected
- {{[[audio]]: this_works_and_is_selected}}
- This is not for some reason
- {{[[audio]]: https://firebasestorage.googleapis.com/v0/b/firescript-577a2.appspot.com/o/imgs%2Fapp%2FMattVogel%2FZ37C1oBWmW.mp3?alt=media&token=433c0926-dbea-47f5-ab4f-3ec4659ba815}}
- binary files
- regular file
- https://firebasestorage.googleapis.com/v0/b/firescript-577a2.appspot.com/o/imgs%2Fapp%2FMattVogel%2FkQXZxVL_6G.txt?alt=media&token=f35c8688-0ff4-4d59-845a-3bcaa53fc1ea
- .zip file
- https://firebasestorage.googleapis.com/v0/b/firescript-577a2.appspot.com/o/imgs%2Fapp%2FMattVogel%2FEzyEq4p2Zn.zip?alt=media&token=0697c12e-654d-46a6-943e-fe13293870ba

View File

@ -16,7 +16,7 @@ This plugin accepts the following configuration options:
- `DONEComponent`: If `true` (default), converts Roam `{{[[DONE]]}}` shortcodes into checked html check boxes.
- `videoComponent`: If `true` (default), converts Roam `{{[[video]]:URL}}` shortcodes into embeded HTML video.
- `audioComponent`: If `true` (default), converts Roam `{{[[audio]]:URL}}` shortcodes into embeded HTML audio.
- `pdfComponent`: If `true` (default), converts Roam `{{[[audio]]:URL}}` shortcodes into embeded HTML PDF viewer.
- `pdfComponent`: If `true` (default), converts Roam `{{[[pdf]]:URL}}` shortcodes into embeded HTML PDF viewer.
- `blockquoteComponent`: If `true` (default), converts Roam `{{[[>]]}}` shortcodes into quartz blockquotes.
## API

View File

@ -63,6 +63,7 @@ const config: QuartzConfig = {
},
keepBackground: false,
}),
Plugin.RoamFlavoredMarkdown(),
Plugin.ObsidianFlavoredMarkdown({ enableInHtmlEmbed: false }),
Plugin.GitHubFlavoredMarkdown(),
Plugin.TableOfContents(),

View File

@ -1,4 +1,9 @@
import { QuartzTransformerPlugin } from "../types"
import { PluggableList } from "unified"
import { SKIP, visit } from "unist-util-visit"
import { Root, Html, BlockContent, DefinitionContent, Paragraph, Code } from "mdast"
import { FilePath, pathToRoot, slugTag, slugifyFilePath } from "../../util/path"
import { ReplaceFunction, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
export interface Options {
orComponent: boolean
@ -30,9 +35,12 @@ 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 multimediaRegex = new RegExp(/{{.*?\b(video|audio)\b.*?\:(.*?)}}/, "g")
const audioRegex = new RegExp(/{{.*?\baudio\b.*?\:(.*?)}}/, "g")
const pdfRegex = new RegExp(/{{.*?\bpdf\b.*?\:(.*?)}}/, "g")
const blockquoteRegex = new RegExp(/\[\[>\]\]/, "g")
const blockquoteRegex = new RegExp(/(\[\[>\]\])\s*(.*)/, "g");
const roamHighlightRegex = new RegExp(/\^\^(.+)\^\^/, "g")
const roamItalicRegex = new RegExp(/__(.+)__/, "g")
const tableRegex = new RegExp(/- {{.*?\btable\b.*?}}/, "g") /* TODO */
@ -46,87 +54,167 @@ export const RoamFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options> | un
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 = `<select class="roam-or-component">${options.map((option: string) => `<option>${option}</option>`).join("")}</select>`
return dropdown
})
}
if (opts.TODOComponent) {
src = src.toString()
src = src.replaceAll(TODORegex, (value, ...capture) => {
const checkbox = '<input type="checkbox" disabled>'
return checkbox
})
}
if (opts.DONEComponent) {
src = src.toString()
src = src.replaceAll(DONERegex, (value, ...capture) => {
const checkbox = '<input type="checkbox" checked disabled>'
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 = `<iframe width="560" height="315" src="https://www.youtube.com/embed/${text}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>`
return video
})
src = src.replaceAll(videoRegex, (value, ...capture) => {
const [match, text] = capture
const video = `<video controls><source src="{${match}}" type="video/mp4"><source src="${match}" type="video/webm"></video>`
return video
})
}
if (opts.audioComponent) {
src = src.toString()
src = src.replaceAll(audioRegex, (value, ...capture) => {
const [match, text] = capture
const audio = `<audio controls>
<source src="${match}" type="audio/mpeg">
<source src="${match}" type="audio/ogg">
Your browser does not support the audio tag.
</audio>`
return audio
})
}
if (opts.pdfComponent) {
src = src.toString()
src = src.replaceAll(pdfRegex, (value, ...capture) => {
const [match, text] = capture
const pdf = `<embed src="${match}" type="application/pdf" width="600" height="800">`
return pdf
})
}
if (opts.blockquoteComponent) {
src = src.toString()
src = src.replaceAll(blockquoteRegex, (value, ...capture) => {
const bq = `>`
return bq
})
}
// if (opts.videoComponent) {
// //youtube first then regular video links
// src = src.toString()
// src = src.replaceAll(youtubeRegex, (value, ...capture) => {
// const [match, text] = capture
// const video = `<iframe width="600px" height="350px" src="https://www.youtube.com/embed/${text}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>`
// return video
// })
// src = src.replaceAll(videoRegex, (value, ...capture) => {
// const [match, text] = capture
// const video = `<video controls><source src="{${match}}" type="video/mp4"><source src="${match}" type="video/webm"></video>`
// return video
// })
// }
// if (opts.audioComponent) {
// src = src.toString()
// src = src.replaceAll(audioRegex, (value, ...capture) => {
// const [match, text] = capture
// const audio = `<audio controls>
// <source src="${match}" type="audio/mpeg">
// <source src="${match}" type="audio/ogg">
// Your browser does not support the audio tag.
// </audio>`
// return audio
// })
// }
// if (opts.pdfComponent) {
// src = src.toString()
// src = src.replaceAll(pdfRegex, (value, ...capture) => {
// const [match, text] = capture
// const pdf = `<embed src="${match}" type="application/pdf" width="600" height="800">`
// 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
if (src instanceof Buffer) {
src = src.toString()
}
src = src.replaceAll(roamItalicRegex, (value, ...capture) => {
const [match, text] = capture
const italic = `*${match}*`
return italic
})
return src
},
markdownPlugins(ctx) {
const plugins: PluggableList = []
const cfg = ctx.cfg.configuration
plugins.push(() => {
return (tree: Root, file) => {
const replacements: [RegExp, string | ReplaceFunction][] = []
const base = pathToRoot(file.data.slug!)
// roam highlight syntax
replacements.push([
roamHighlightRegex,
(_value: string, ...capture: string[]) => {
const [inner] = capture
return {
type: "html",
value: `<span class="text-highlight"> ${inner} </span>`,
}
},
])
if (opts.orComponent) {
replacements.push([
orRegex,
(match: string) => {
// Attempt to extract the content within the curly braces and split by '|'
const matchResult = match.match(/{{or:(.*?)}}/);
if (matchResult === null) {
// Handle the case where no match is found, e.g., return an empty string or some default value
return { type: "html", value: "" };
}
const optionsString: string = matchResult[1];
const options: string[] = optionsString.split("|");
// Generate the HTML <select> element with options
const selectHtml: string = `<select>${options.map((option: string) => `<option value="${option}">${option}</option>`).join("")}</select>`;
return {
type: "html",
value: selectHtml,
};
},
]);
}
if (opts.TODOComponent) {
replacements.push([
TODORegex,
(match) => {
return {
type: "html",
value: `<input type="checkbox" disabled>`,
}
},
])
}
if (opts.DONEComponent) {
replacements.push([
DONERegex,
(match) => {
return {
type: "html",
value: `<input type="checkbox" checked disabled>`,
}
},
])
}
if (opts.blockquoteComponent) {
replacements.push([
blockquoteRegex,
(match, marker, content) => {
const blockquoteHtml = `<blockquote>${content.trim()}</blockquote>`;
return {
type: "html",
value: blockquoteHtml,
};
},
]);
}
if (opts.audioComponent) {
replacements.push([
audioRegex,
(_value: string, ...capture: string[]) => {
const [url] = capture;
console.log("");
console.log(" ~", capture);
const audioHtml = `<audio controls>
<source src="${url}" type="audio/mpeg">
<source src="${url}" type="audio/ogg">
Your browser does not support the audio tag.
</audio>`;
return {
type: "html",
value: audioHtml,
};
}
]);
}
mdastFindReplace(tree, replacements)
}
})
return plugins
},
}
}