This commit is contained in:
Alex Domingo 2026-03-22 02:10:32 +01:00 committed by GitHub
commit 77d5669c0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 101 additions and 0 deletions

View File

@ -72,6 +72,7 @@ const config: QuartzConfig = {
Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }), Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }),
Plugin.Description(), Plugin.Description(),
Plugin.Latex({ renderEngine: "katex" }), Plugin.Latex({ renderEngine: "katex" }),
Plugin.LucideIcons(),
], ],
filters: [Plugin.RemoveDrafts()], filters: [Plugin.RemoveDrafts()],
emitters: [ emitters: [

View File

@ -11,3 +11,4 @@ export { SyntaxHighlighting } from "./syntax"
export { TableOfContents } from "./toc" export { TableOfContents } from "./toc"
export { HardLineBreaks } from "./linebreaks" export { HardLineBreaks } from "./linebreaks"
export { RoamFlavoredMarkdown } from "./roam" export { RoamFlavoredMarkdown } from "./roam"
export { LucideIcons } from "./lucide"

View File

@ -0,0 +1,93 @@
import { Element, Literal, Root } from "hast"
import { visit } from "unist-util-visit"
import { QuartzTransformerPlugin } from "../types"
function rehypeLucideIcons(verbose: boolean = false) {
return (tree: Root) => {
visit(tree, "text", (node: Literal, index: number | undefined, parent: any) => {
if (
typeof node.value === "string" &&
typeof index === "number" &&
parent &&
parent.children &&
Array.isArray(parent.children)
) {
// Replace the first match of a Lucide icon tag in this node.
// Once the node is updated with the icon element, it will trigger a
// new visit call to this node and recursively replace all icon tags.
const originalText = node.value
const lucidePattern = /:luc_([a-z_]+):/
const lucideTag = originalText.match(lucidePattern)
if (lucideTag) {
const [iconTag, iconName] = lucideTag
const tagStart = lucideTag.index ?? 0
let replacedText: Array<Literal | Element> = []
if (tagStart > 0) {
replacedText.push({
type: "text",
value: originalText.substring(0, tagStart),
})
}
const lucideIconElement: Element = {
type: "element",
tagName: "i",
properties: {
class: `lucide lucide-${iconName}`,
"data-lucide": iconName,
"aria-hidden": "true",
},
children: [],
}
replacedText.push(lucideIconElement)
if (verbose) {
console.log(
`[LucideIcons] Replaced markdown :luc_${iconName}: with HTML Lucide icon "${iconName}"`,
)
}
const remainingText = originalText.substring(tagStart + iconTag.length)
if (remainingText) {
replacedText.push({
type: "text",
value: remainingText,
})
}
parent.children.splice(index, 1, ...replacedText)
}
}
})
}
}
export const LucideIcons: QuartzTransformerPlugin = () => {
return {
name: "LucideIcons",
htmlPlugins(ctx) {
return [() => rehypeLucideIcons(ctx?.argv?.verbose || false)]
},
externalResources() {
return {
js: [
{
src: "https://unpkg.com/lucide@latest",
loadTime: "afterDOMReady",
contentType: "external",
},
{
script: "lucide.createIcons();",
loadTime: "afterDOMReady",
contentType: "inline",
},
],
}
},
}
}
export default LucideIcons

View File

@ -639,3 +639,9 @@ iframe.pdf {
transition: width 0.2s ease; transition: width 0.2s ease;
z-index: 9999; z-index: 9999;
} }
.lucide {
/* render Lucide icons proportional to font size */
width: 1em;
height: 1em;
}