Utensil's digital garden of Obsidian compatible notes via Quartz utensil.tngl.sh/garden/
0

Configure Feed

Select the types of activity you want to include in your feed.

Add transformer plugin: IframeEmbed

utensil (Jul 20, 2025, 5:33 PM +0800) ac76ff17 06802a18

+80
+1
quartz.config.ts
··· 80 80 Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }), 81 81 Plugin.Description(), 82 82 Plugin.Latex({ renderEngine: "katex" }), 83 + Plugin.IframeEmbed(), 83 84 ], 84 85 filters: [Plugin.RemoveDrafts()], 85 86 emitters: [
+78
quartz/plugins/transformers/iframe-embed.ts
··· 1 + import { QuartzTransformerPlugin } from "../types" 2 + import { Root } from "mdast" 3 + import { Element, Root as HtmlRoot } from "hast" 4 + import rehypeRaw from "rehype-raw" 5 + import { visit } from "unist-util-visit" 6 + import { PluggableList } from "unified" 7 + 8 + export interface Options { 9 + enableIframeEmbed: boolean 10 + } 11 + 12 + const defaultOptions: Options = { 13 + enableIframeEmbed: true, 14 + } 15 + 16 + export const IframeEmbed: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => { 17 + const opts = { ...defaultOptions, ...userOpts } 18 + 19 + return { 20 + name: "IframeEmbed", 21 + htmlPlugins() { 22 + const plugins: PluggableList = [rehypeRaw] 23 + 24 + if (opts.enableIframeEmbed) { 25 + plugins.push(() => { 26 + return (tree: HtmlRoot, file) => { 27 + visit(tree, "element", (node: Element) => { 28 + if (node.tagName === "iframe") { 29 + // Extract src and style attributes 30 + const src = node.properties.src as string 31 + let styleObj: Record<string, string | number> = {} 32 + 33 + // Parse style attribute if it exists 34 + if (node.properties.style && typeof node.properties.style === "string") { 35 + const styleStr = node.properties.style as string 36 + const styleEntries = styleStr.split(';') 37 + .filter(entry => entry.trim() !== '') 38 + .map(entry => { 39 + const [key, value] = entry.split(':').map(part => part.trim()) 40 + return [key, value] 41 + }) 42 + 43 + styleObj = Object.fromEntries(styleEntries) 44 + } 45 + 46 + // Store iframe data in file frontmatter for the Iframe component to use 47 + if (!file.data.frontmatter) { 48 + file.data.frontmatter = {} 49 + } 50 + 51 + file.data.frontmatter.iframe = src 52 + file.data.frontmatter["iframe-style"] = styleObj 53 + 54 + // Replace the iframe with a div that will be rendered by the Iframe component 55 + node.tagName = "div" 56 + node.properties = { 57 + ...node.properties, 58 + class: "quartz-iframe", 59 + "data-iframe-src": src, 60 + } 61 + 62 + // Clear children to ensure proper rendering 63 + node.children = [] 64 + } 65 + }) 66 + } 67 + }) 68 + } 69 + 70 + return plugins 71 + }, 72 + markdownPlugins() { 73 + return [] 74 + }, 75 + } 76 + } 77 + 78 + export default IframeEmbed
+1
quartz/plugins/transformers/index.ts
··· 11 11 export { TableOfContents } from "./toc" 12 12 export { HardLineBreaks } from "./linebreaks" 13 13 export { RoamFlavoredMarkdown } from "./roam" 14 + export { IframeEmbed } from "./iframe-embed"