diff --git a/docs/features/Obsidian compatibility.md b/docs/features/Obsidian compatibility.md index 61fcc060d..d519e1779 100644 --- a/docs/features/Obsidian compatibility.md +++ b/docs/features/Obsidian compatibility.md @@ -26,6 +26,7 @@ Finally, Quartz also provides `Plugin.CrawlLinks` which allows you to customize - `mermaid`: whether to enable [[Mermaid diagrams]]. Defaults to `true` - `parseTags`: whether to try and parse tags in the content body. Defaults to `true` - `enableInHtmlEmbed`: whether to try and parse Obsidian flavoured markdown in raw HTML. Defaults to `false` + - `enableYouTubeEmbed`: whether to enable embedded YouTube videos using external image Markdown syntax. Defaults to `false` - Link resolution behaviour: - Disabling: remove all instances of `Plugin.CrawlLinks()` from `quartz.config.ts` - Changing link resolution preference: set `markdownLinkResolution` to one of `absolute`, `relative` or `shortest` diff --git a/quartz/components/styles/explorer.scss b/quartz/components/styles/explorer.scss index ff046a665..304fd4500 100644 --- a/quartz/components/styles/explorer.scss +++ b/quartz/components/styles/explorer.scss @@ -126,7 +126,7 @@ svg { backface-visibility: visible; } -div:has(> .folder-outer:not(.open)) > .folder-container > svg { +li:has(> .folder-outer:not(.open)) > .folder-container > svg { transform: rotate(-90deg); } diff --git a/quartz/plugins/filters/explicit.ts b/quartz/plugins/filters/explicit.ts index 30f0b37fa..48f92bdf2 100644 --- a/quartz/plugins/filters/explicit.ts +++ b/quartz/plugins/filters/explicit.ts @@ -3,7 +3,11 @@ import { QuartzFilterPlugin } from "../types" export const ExplicitPublish: QuartzFilterPlugin = () => ({ name: "ExplicitPublish", shouldPublish(_ctx, [_tree, vfile]) { - const publishFlag: boolean = vfile.data?.frontmatter?.publish ?? false + const publishProperty = vfile.data?.frontmatter?.publish ?? false + const publishFlag = + typeof publishProperty === "string" + ? publishProperty.toLowerCase() === "true" + : Boolean(publishProperty) return publishFlag }, }) diff --git a/quartz/plugins/transformers/lastmod.ts b/quartz/plugins/transformers/lastmod.ts index feca4b52f..6e12616f7 100644 --- a/quartz/plugins/transformers/lastmod.ts +++ b/quartz/plugins/transformers/lastmod.ts @@ -57,10 +57,22 @@ export const CreatedModifiedDate: QuartzTransformerPlugin | und published ||= file.data.frontmatter.publishDate } else if (source === "git") { if (!repo) { - repo = new Repository(file.cwd) + // Get a reference to the main git repo. + // It's either the same as the workdir, + // or 1+ level higher in case of a submodule/subtree setup + repo = Repository.discover(file.cwd) } - modified ||= await repo.getFileLatestModifiedDateAsync(file.data.filePath!) + try { + modified ||= await repo.getFileLatestModifiedDateAsync(file.data.filePath!) + } catch { + console.log( + chalk.yellow( + `\nWarning: ${file.data + .filePath!} isn't yet tracked by git, last modification date is not available for this file`, + ), + ) + } } } diff --git a/quartz/plugins/transformers/ofm.ts b/quartz/plugins/transformers/ofm.ts index 5aa09c5bb..be3344aa4 100644 --- a/quartz/plugins/transformers/ofm.ts +++ b/quartz/plugins/transformers/ofm.ts @@ -25,6 +25,7 @@ export interface Options { parseTags: boolean parseBlockReferences: boolean enableInHtmlEmbed: boolean + enableYouTubeEmbed: boolean } const defaultOptions: Options = { @@ -36,6 +37,7 @@ const defaultOptions: Options = { parseTags: true, parseBlockReferences: true, enableInHtmlEmbed: false, + enableYouTubeEmbed: false, } const icons = { @@ -127,6 +129,7 @@ const calloutLineRegex = new RegExp(/^> *\[\!\w+\][+-]?.*$/, "gm") // (?:\/[-_\p{L}\d\p{Z}]+)*) -> non-capturing group, matches an arbitrary number of tag strings separated by "/" const tagRegex = new RegExp(/(?:^| )#((?:[-_\p{L}\p{Emoji}\d])+(?:\/[-_\p{L}\p{Emoji}\d]+)*)/, "gu") const blockReferenceRegex = new RegExp(/\^([A-Za-z0-9]+)$/, "g") +const ytLinkRegex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin | undefined> = ( userOpts, @@ -201,7 +204,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin if (value.startsWith("!")) { const ext: string = path.extname(fp).toLowerCase() const url = slugifyFilePath(fp as FilePath) - if ([".png", ".jpg", ".jpeg", ".gif", ".bmp", ".svg"].includes(ext)) { + if ([".png", ".jpg", ".jpeg", ".gif", ".bmp", ".svg", ".webp"].includes(ext)) { const dims = alias ?? "" let [width, height] = dims.split("x", 2) width ||= "auto" @@ -505,6 +508,30 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin }) } + if (opts.enableYouTubeEmbed) { + plugins.push(() => { + return (tree: HtmlRoot) => { + visit(tree, "element", (node) => { + if (node.tagName === "img" && typeof node.properties.src === "string") { + const match = node.properties.src.match(ytLinkRegex) + const videoId = match && match[2].length == 11 ? match[2] : null + if (videoId) { + node.tagName = "iframe" + node.properties = { + class: "external-embed", + allow: "fullscreen", + frameborder: 0, + width: "600px", + height: "350px", + src: `https://www.youtube.com/embed/${videoId}`, + } + } + } + }) + } + }) + } + return plugins }, externalResources() {