From 86052c0c1344eb2b8384c04b6862df58478a4ce9 Mon Sep 17 00:00:00 2001 From: WinnerWind <71366717+WinnerWind@users.noreply.github.com> Date: Sun, 1 Jun 2025 17:01:05 +0530 Subject: [PATCH 01/13] Add badges plugin with code and CSS. --- package-lock.json | 8 + package.json | 3 +- quartz.config.ts | 1 + quartz/plugins/transformers/badges.ts | 233 ++++++++++++++ quartz/plugins/transformers/index.ts | 1 + quartz/styles/badges.scss | 421 ++++++++++++++++++++++++++ 6 files changed, 666 insertions(+), 1 deletion(-) create mode 100644 quartz/plugins/transformers/badges.ts create mode 100644 quartz/styles/badges.scss diff --git a/package-lock.json b/package-lock.json index 8bd2ac45c..c51371fdf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -85,6 +85,7 @@ "@types/ws": "^8.18.1", "@types/yargs": "^17.0.33", "esbuild": "^0.25.5", + "lucide-static": "0.511.0", "prettier": "^3.5.3", "tsx": "^4.19.4", "typescript": "^5.8.3" @@ -4138,6 +4139,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/lucide-static": { + "version": "0.511.0", + "resolved": "https://registry.npmjs.org/lucide-static/-/lucide-static-0.511.0.tgz", + "integrity": "sha512-/MWOjEyGZO84B16BeqbeleinbmeYxOBsbYDt/Yk6xGwMYwDm6dx43jKHMxGDqW9U84qWL13dNvVW0SMADhUSyg==", + "dev": true, + "license": "ISC" + }, "node_modules/markdown-table": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", diff --git a/package.json b/package.json index ba07693d5..6974cac24 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,7 @@ "esbuild": "^0.25.5", "prettier": "^3.5.3", "tsx": "^4.19.4", - "typescript": "^5.8.3" + "typescript": "^5.8.3", + "lucide-static": "0.511.0" } } diff --git a/quartz.config.ts b/quartz.config.ts index b3db3d60d..10df0a274 100644 --- a/quartz.config.ts +++ b/quartz.config.ts @@ -72,6 +72,7 @@ const config: QuartzConfig = { Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }), Plugin.Description(), Plugin.Latex({ renderEngine: "katex" }), + Plugin.InlineBadges(), ], filters: [Plugin.RemoveDrafts()], emitters: [ diff --git a/quartz/plugins/transformers/badges.ts b/quartz/plugins/transformers/badges.ts new file mode 100644 index 000000000..28d3c9b6f --- /dev/null +++ b/quartz/plugins/transformers/badges.ts @@ -0,0 +1,233 @@ +import { QuartzTransformerPlugin } from "../types" +import badgesCSS from "../../styles/badges.scss" +import { JSResource, CSSResource } from "../../util/resources" +import icons from "lucide-static" + +export const BADGE_TYPES: any[] = [ + ["note", "Note", "lucide-pencil"], + ["info", "Info", "lucide-info"], + ["todo", "Todo", "lucide-check-circle-2"], + ["abstract", "Abstract", "lucide-clipboard-list"], + ["summary", "Summary", "lucide-clipboard-list"], + ["tldr", "TLDR", "lucide-clipboard-list"], + ["tip", "Tip", "lucide-flame"], + ["hint", "Hint", "lucide-flame"], + ["important", "Important", "lucide-flame"], + ["success", "Success", "lucide-check"], + ["check", "Check", "lucide-check"], + ["done", "Done", "lucide-check"], + ["question", "Question", "help-circle"], + ["help", "Help", "help-circle"], + ["faq", "FAQ", "help-circle"], + ["warning", "Warning", "lucide-alert-triangle"], + ["caution", "Caution", "lucide-alert-triangle"], + ["attention", "Attention", "lucide-alert-triangle"], + ["failure", "Failure", "lucide-x"], + ["fail", "Fail", "lucide-x"], + ["missing", "Missing", "lucide-x"], + ["danger", "Danger", "lucide-zap"], + ["error", "Error", "lucide-zap"], + ["bug", "Bug", "lucide-bug"], + ["example", "Example", "lucide-list"], + ["quote", "Quote", "quote-glyph"], + ["cite", "Cite", "quote-glyph"], + ["power", "Power", "lucide-power"], + ["verse", "Verse", "lucide-music"], + ["complete", "Complete", "lucide-check-circle"], + ["milestone", "Milestone", "lucide-milestone"], + ["component", "Component", "lucide-toy-brick"], + ["polish", "Polish", "lucide-car"], + ["point", "point", "lucide-pointer"], + ["dream", "Dream", "lucide-moon"], + ["process", "Process", "lucide-clock"], + ["refine", "Refine", "lucide-axe"], + ["image", "Image", "lucide-image"], + ["party", "Party", "lucide-party-popper"], + ["crystallize", "Crystallize", "lucide-diamond"], + ["definition", "Definition", "lucide-key"], + ["mention", "Mention", "lucide-at-sign"], + ["exclaim", "Exclaim", "lucide-megaphone"], + ["meta", "Meta", "lucide-filter"], + ["compute", "Compute", "lucide-hourglass"], + ["emergency", "Emergency", "lucide-siren"], + ["magnet", "Magnet", "lucide-magnet"], + ["flag", "Flag", "flag"], + ["branch", "Branch", "network"], + ["snippet", "Snippet", "scissors"], + ["lock", "Lock", "lock"], + ["highlight", "Highlight", "highlighter"], + ["clue", "Clue", "puzzle"], + ["claim", "Claim", "anchor"], + ["profile", "Profile", "lucide-user"], + ["hat-tip", "Hat-tip", "hard-hat"], + ["dig", "Dig", "shovel"], + ["witness", "Witness", "edit-3"], + ["notice", "Notice", "pen-tool"], + ["attachment", "Attachment", "paperclip"], + ["lightbulb", "Lightbulb", "lightbulb"], + ["prohibit", "Prohibit", "ban"], + ["stop", "Stop", "lucide-alert-octagon"], + ["bomb", "Bomb", "lucide-bomb"], + ["hold", "Hold", "lucide-hand"], + ["charge", "Charge", "lucide-zap"], + ["sprout", "Sprout", "lucide-sprout"], + ["extract", "Extract", "lucide-hammer"], + ["compass", "Compass", "lucide-compass"], + ["map", "Map", "lucide-map"], + ["expedition", "Expedition", "lucide-mountain-snow"], + ["home", "Home", "lucide-home"], + ["knowledge", "Knowledge", "lucide-book"], + ["account", "Account", "open-vault"], + ["judgment", "Judgment", "lucide-gavel"], + ["balance", "Balance", "lucide-scale"], + ["feast", "Feast", "lucide-grape"], + ["gift", "Gift", "lucide-gift"], + ["love", "Love", "lucide-heart"], + ["specimen", "Specimen", "lucide-gem"], + ["command", "Command", "lucide-swords"], + ["deed", "Deed", "lucide-scroll"], + ["honor", "Honor", "lucide-sword"], + ["reward", "Reward", "lucide-crown"], + ["customized", "Customized", "hash"], + ["vault", "Vault", "vault"], +] + +const REGEXP = /\[!!([^\]]+)\]/gm +const CODEREGEX = /`([^`\n]+)`/g + +// This plugin has no options. +export interface Options { + dummyOption: boolean +} + +export const InlineBadges: QuartzTransformerPlugin> = (_userOpts) => { + return { + name: "InlineBadges", + textTransform(_ctx, src) { + var srcReplacement: string = src //Start by assuming there are no badges. + for (const match of src.matchAll(CODEREGEX)) { + for (const badgeMatch of match[0].matchAll(REGEXP)) { + srcReplacement = srcReplacement.replace(match[0], buildBadge(badgeMatch[0])) + } + } + return srcReplacement + }, + externalResources() { + // Required for proper rendering under themes. + const js: JSResource[] = [] + const css: CSSResource[] = [] + + css.push({ + content: badgesCSS, + inline: true, + }) + css.push({ + // Requirement for the SVG to align properly. + content: ".inline-badge .inline-badge-icon svg {display: flex;align-self: center;}", + inline: true, + }) + return { js, css } + }, + } +} + +function buildBadge(text: string) { + // HTML Elements + let newEl = "" + let iconEl = "" + let titleEl = "" + let textEl = "" + let attrType = "" + let part: string = text?.substring(2) ?? "" + let content: string = part?.substring(part.length - 1, 1).trim() ?? "" + let styleAttr = "" + + // no content + if (!content.length) { + newEl = "Badges syntax error" + return newEl + } + + let parts: string[] = content.split(":", 2) + // return if NO CONTENT + if (parts.length < 2) { + newEl = `❌ Badges syntax error` + return newEl + } + + // type of badge + let badgeType: string = parts[0].trim() + // build and check for extras + let extras: string[] = badgeType.split("|") + let hasExtra: boolean = extras.length > 1 + // title value for badge + let badgeContent: string = parts[1].trim().split("|")[0] //Original code doesnt have .split[...] but for some reason its needed for proper rendering. + + // custom badge + if (extras.length == 3) { + //icon + iconEl = `${getLucideIconSvg(extras[1])}` + attrType = "customized" + + // details + let details: any[] = parts[1].split("|") + + //title + let title: string = details[0].trim() + titleEl = `${title}` + + // color + let color: string = "currentColor" + if (details[1]) { + color = details[1].trim() + } + + styleAttr = `style="--customize-badge-color: ${color};"` + } else { + if (hasExtra) { + // Github badges + if (extras[1].startsWith("ghb>") || extras[1].startsWith("ghs>")) { + let ghType: string = extras[1].split(">")[1].trim() + iconEl = `${getLucideIconSvg("github")}` + textEl = `${ghType}` + attrType = extras[1].startsWith("ghb>") ? "github" : "github-success" + badgeType = extras[1].startsWith("ghb>") ? "github" : "github-success" + } else { + // No icon, text only + iconEl = `${badgeType.split("|")[1].trim()}` + attrType = "text" + badgeType = "text" + } + } else { + // Non-github + attrType = badgeType.trim() + BADGE_TYPES.forEach((el) => { + if (el.indexOf(badgeType.toLowerCase()) == 0 && el[2].length > 0) { + iconEl = `${getLucideIconSvg(el[2])}` + } + }) + } + } + // render + titleEl = `${badgeContent}` + newEl = `${iconEl}${textEl}${titleEl}` + return newEl +} + +function getLucideIconSvg(iconName: string): string { + var potentialIconReturn = + icons[toPascalCase(iconName.replace(/^lucide-/, "")) as keyof typeof icons] + if (potentialIconReturn) { + var iconToReturn = potentialIconReturn.replace(/\n/g, "").replace(" ", " ") + return iconToReturn + } else { + return `` + } +} + +function toPascalCase(str: string): string { + // First, convert kebab-case to camelCase + const camelCaseStr = str.replace(/-([a-z])/g, (g) => g[1].toUpperCase()) + // Then, capitalize the first letter to make it PascalCase + return camelCaseStr.charAt(0).toUpperCase() + camelCaseStr.slice(1) +} diff --git a/quartz/plugins/transformers/index.ts b/quartz/plugins/transformers/index.ts index 8e2cd844f..58b5df5c1 100644 --- a/quartz/plugins/transformers/index.ts +++ b/quartz/plugins/transformers/index.ts @@ -11,3 +11,4 @@ export { SyntaxHighlighting } from "./syntax" export { TableOfContents } from "./toc" export { HardLineBreaks } from "./linebreaks" export { RoamFlavoredMarkdown } from "./roam" +export { InlineBadges } from "./badges" diff --git a/quartz/styles/badges.scss b/quartz/styles/badges.scss new file mode 100644 index 000000000..54d03e9f6 --- /dev/null +++ b/quartz/styles/badges.scss @@ -0,0 +1,421 @@ +body { + --inline-badge-border-color: transparent; + --inline-badge-font-size: 0.8em; + --inline-badge-border-radius: var(--radius-s); + --inline-badge-border: 1px solid var(--inline-badge-border-color); + --my-custom-rgb: var(--color-green-rgb); +} +.inline-badge { + display: inline-flex; + margin: 0; + padding: 0; + vertical-align: middle; + border-radius: var(--inline-badge-border-radius); + font-weight: 500; + border: var(--inline-badge-border); + cursor: default; +} +.inline-badge .inline-badge-extra::after { + content: ":"; +} +.inline-badge .gh-type { + display: inline-block !important; + font-size: var(--inline-badge-font-size); + padding-right: 0.5em; + padding-top: 0.15em; + /* color: var(--color-blue); */ +} +.inline-badge .inline-badge-extra { + display: inline-block !important; + font-size: var(--inline-badge-font-size); + padding-inline: 0.5em; + padding-top: 0.15em; + font-family: inherit; +} +.inline-badge .inline-badge-icon { + padding: 0.3em; +} +.inline-badge .inline-badge-icon svg { + width: 14px; + height: 14px; +} +.inline-badge .inline-badge-extra, +.inline-badge .inline-badge-icon { + display: inline-flex !important; +} +.inline-badge .inline-badge-title-inner { + display: inline-block !important; + font-size: var(--inline-badge-font-size); + font-weight: inherit; + padding-right: 5px; + padding-top: 2px; +} +.inline-badge[data-inline-badge="info"], +.inline-badge[data-inline-badge="todo"], +.inline-badge[data-inline-badge="note"] { + color: rgba(var(--color-blue-rgb), 1); + background-color: rgba(var(--color-blue-rgb), 0.123); +} +.inline-badge[data-inline-badge="example"] { + color: rgba(var(--color-purple-rgb), 1); + background-color: rgba(var(--color-purple-rgb), 0.123); +} +.inline-badge[data-inline-badge="abstract"], +.inline-badge[data-inline-badge="summary"], +.inline-badge[data-inline-badge="tldr"], +.inline-badge[data-inline-badge="tip"], +.inline-badge[data-inline-badge="hint"], +.inline-badge[data-inline-badge="important"] { + color: rgba(var(--color-cyan-rgb), 1); + background-color: rgba(var(--color-cyan-rgb), 0.133); +} +.inline-badge[data-inline-badge="success"], +.inline-badge[data-inline-badge="check"], +.inline-badge[data-inline-badge="done"] { + color: rgba(var(--color-green-rgb), 1); + background-color: rgba(var(--color-green-rgb), 0.123); +} +.inline-badge[data-inline-badge="question"], +.inline-badge[data-inline-badge="help"], +.inline-badge[data-inline-badge="faq"] { + color: rgba(var(--color-yellow-rgb), 1); + background-color: rgba(var(--color-yellow-rgb), 0.123); +} +.inline-badge[data-inline-badge="warning"], +.inline-badge[data-inline-badge="caution"], +.inline-badge[data-inline-badge="attention"] { + color: rgba(var(--color-orange-rgb), 1); + background-color: rgba(var(--color-orange-rgb), 0.123); +} +.inline-badge[data-inline-badge="failure"], +.inline-badge[data-inline-badge="fail"], +.inline-badge[data-inline-badge="missing"], +.inline-badge[data-inline-badge="bug"], +.inline-badge[data-inline-badge="error"], +.inline-badge[data-inline-badge="danger"] { + color: rgba(var(--color-red-rgb), 1); + background-color: rgba(var(--color-red-rgb), 0.123); +} +.inline-badge[data-inline-badge^="power"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 70, 164, 115; +} +.inline-badge[data-inline-badge^="verse"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 80, 181, 132; +} +.inline-badge[data-inline-badge^="complete"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 78, 182, 127; +} +.inline-badge[data-inline-badge^="milestone"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 70, 164, 115; +} +.inline-badge[data-inline-badge^="component"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 22, 195, 221; +} +.inline-badge[data-inline-badge^="polish"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 26, 194, 225; +} +.inline-badge[data-inline-badge^="refine"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 0, 177, 206; +} +.inline-badge[data-inline-badge^="process"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 0, 176, 210; +} +.inline-badge[data-inline-badge^="dream"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 0, 179, 194; +} +.inline-badge[data-inline-badge^="point"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 0, 196, 215; +} +.inline-badge[data-inline-badge^="crystallize"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 0, 160, 190; +} +.inline-badge[data-inline-badge^="party"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 0, 160, 186; +} +.inline-badge[data-inline-badge^="image"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 0, 162, 175; +} +.inline-badge[data-inline-badge^="compute"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); + --badge-color: 97, 163, 230; +} +.inline-badge[data-inline-badge^="meta"] { + --badge-color: 100, 141, 213; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.168); +} +.inline-badge[data-inline-badge^="definition"] { + --badge-color: 122, 155, 236; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="expedition"] { + --badge-color: 100, 141, 213; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="map"] { + --badge-color: 140, 150, 236; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge^="compass"] { + --badge-color: 140, 150, 236; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge^="knowledge"] { + --badge-color: 97, 163, 230; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge^="home"] { + --badge-color: 182, 156, 246; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge^="account"] { + --badge-color: 204, 148, 230; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge^="judgment"] { + --badge-color: 148, 129, 204; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge^="balance"] { + --badge-color: 164, 143, 225; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge^="feast"] { + --badge-color: 182, 156, 246; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge^="gift"] { + --badge-color: 204, 148, 230; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge^="love"] { + --badge-color: 208, 128, 182; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge^="specimen"] { + --badge-color: 208, 126, 186; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge^="command"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); + --badge-color: 189, 114, 168; +} +.inline-badge[data-inline-badge^="deed"] { + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); + --badge-color: 229, 140, 197; +} +.inline-badge[data-inline-badge^="honor"] { + --badge-color: 229, 140, 197; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge^="reward"] { + --badge-color: 164, 143, 225; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.177); +} +.inline-badge[data-inline-badge="customized"] { + color: rgba(var(--customize-badge-color), 1); + background-color: rgba(var(--customize-badge-color), 0.177); +} +.inline-badge[data-inline-badge^="claim"] { + --badge-color: 214, 139, 71; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="clue"] { + --badge-color: 206, 144, 66; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="highlight"] { + --badge-color: 186, 130, 58; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="lock"] { + --badge-color: 223, 124, 142; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="snippet"] { + --badge-color: 186, 130, 58; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="branch"] { + --badge-color: 186, 130, 58; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="flag"] { + --badge-color: 241, 138, 161; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="dig"] { + --badge-color: 163, 143, 45; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="profile"] { + --badge-color: 224, 159, 71; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="notice"] { + --badge-color: 199, 173, 64; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="witness"] { + --badge-color: 146, 150, 58; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="attachment"] { + --badge-color: 146, 150, 58; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="lightbulb"] { + --badge-color: 180, 180, 74; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="hat-tip"] { + --badge-color: 180, 158, 51; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="magnet"] { + --badge-color: 225, 129, 99; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="emergency"] { + --badge-color: 202, 112, 129; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="prohibit"] { + --badge-color: 245, 140, 129; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="stop"] { + --badge-color: 245, 140, 129; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="bomb"] { + --badge-color: 223, 127, 120; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="extract"] { + --badge-color: 78, 182, 127; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="sprout"] { + --badge-color: 97, 198, 138; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="charge"] { + --badge-color: 84, 199, 148; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="hold"] { + --badge-color: 88, 199, 146; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="mention"] { + --badge-color: var(--color-blue-rgb); + color: rgba(var(--badge-color), 0.8); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="exclaim"] { + --badge-color: var(--color-green-rgb); + color: rgba(var(--badge-color), 0.8); + background-color: rgba(var(--badge-color), 0.123); +} +.inline-badge[data-inline-badge^="vault"] { + --badge-color: var(--color-purple-rgb); + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +/* Github */ +.inline-badge[data-inline-badge="github"] .gh-type { + color: var(--color-blue-rgb) !important; +} +.inline-badge[data-inline-badge="github"] .inline-badge-title-inner { + color: var(--text-normal); +} +.inline-badge[data-inline-badge="github"] { + --badge-color: var(--color-blue-rgb); + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} +/* Github SUCCESS */ +.inline-badge[data-inline-badge^="github-success"] .gh-type { + color: var(--color-green-rgb) !important; +} +.inline-badge[data-inline-badge^="github-success"] .inline-badge-title-inner { + color: var(--text-normal); +} +.inline-badge[data-inline-badge^="github-success"] { + --badge-color: var(--color-green-rgb); + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.144); +} +.inline-badge[data-inline-badge^="brain"] { + --badge-color: 206, 144, 66; + color: rgba(var(--badge-color), 1); + background-color: rgba(var(--badge-color), 0.123); +} From 4eba49b51aae4da92b209bc34baad8b6f22c8ff9 Mon Sep 17 00:00:00 2001 From: WinnerWind <71366717+WinnerWind@users.noreply.github.com> Date: Tue, 15 Jul 2025 09:05:39 +0530 Subject: [PATCH 02/13] Allow definition of custom badges via settings Custom badges can be defined via `quartz.config.ts` by adding the `customBadge` option, an array. Eg. ``` Plugin.InlineBadges({ customBadges: [["menu", "menu-test", [140, 200, 110,1], 0.117], ["toggle-right","switch-on",[225, 0, 40,1],0.117] ], }), ``` Array is defined in the format ``` [ [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA], [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA] ] ``` --- quartz/plugins/transformers/badges.ts | 34 +++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/quartz/plugins/transformers/badges.ts b/quartz/plugins/transformers/badges.ts index 28d3c9b6f..52229a7b4 100644 --- a/quartz/plugins/transformers/badges.ts +++ b/quartz/plugins/transformers/badges.ts @@ -92,18 +92,30 @@ export const BADGE_TYPES: any[] = [ ["vault", "Vault", "vault"], ] +var allBadges: any[] = BADGE_TYPES //Append custom badges to the end of this array. + const REGEXP = /\[!!([^\]]+)\]/gm const CODEREGEX = /`([^`\n]+)`/g -// This plugin has no options. export interface Options { - dummyOption: boolean + customBadges: Array, // Write in format [ [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA], [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA] ] } -export const InlineBadges: QuartzTransformerPlugin> = (_userOpts) => { +const defaultOptions: Options = { + customBadges: [] +} + +export const InlineBadges: QuartzTransformerPlugin> = (userOpts) => { + const opts = { ...defaultOptions, ...userOpts } + return { name: "InlineBadges", textTransform(_ctx, src) { + // Append custom badges. + for (let badge of opts.customBadges){ + allBadges.push([badge[1],badge[1],badge[0]]) // Pushes it to the array in the format [icon,icon,name]. + } + var srcReplacement: string = src //Start by assuming there are no badges. for (const match of src.matchAll(CODEREGEX)) { for (const badgeMatch of match[0].matchAll(REGEXP)) { @@ -126,6 +138,20 @@ export const InlineBadges: QuartzTransformerPlugin> = (_userOpt content: ".inline-badge .inline-badge-icon svg {display: flex;align-self: center;}", inline: true, }) + // Sets the colour of custom badges. + for (let badgeDef of opts.customBadges){ + let badgeColor: string = `${badgeDef[2][0]},${badgeDef[2][1]},${badgeDef[2][2]}` + let textColor: string = `rgba(var(--badge-color),${badgeDef[3]})` + let badgeName: string = badgeDef[1] // Removes the "" from the name. + css.push({ + content: `.inline-badge[data-inline-badge=${badgeName}] { + --badge-color: ${badgeColor}; + color: rgba(var(--badge-color), ${badgeDef[2][3]}); + background-color: ${textColor}; + }`, + inline: true, + }) + } return { js, css } }, } @@ -201,7 +227,7 @@ function buildBadge(text: string) { } else { // Non-github attrType = badgeType.trim() - BADGE_TYPES.forEach((el) => { + allBadges.forEach((el) => { if (el.indexOf(badgeType.toLowerCase()) == 0 && el[2].length > 0) { iconEl = `${getLucideIconSvg(el[2])}` } From 72a8e4b8c7f0bb10117f387f833291ff5f418444 Mon Sep 17 00:00:00 2001 From: WinnerWind <71366717+WinnerWind@users.noreply.github.com> Date: Tue, 15 Jul 2025 10:26:38 +0530 Subject: [PATCH 03/13] Add type safety to customBadges please be happy github actions `integer` not `int` How did my code compile? Use proper touple syntax Ok Fix array type for customBadges this took too long. --- quartz/plugins/transformers/badges.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quartz/plugins/transformers/badges.ts b/quartz/plugins/transformers/badges.ts index 52229a7b4..789c2f459 100644 --- a/quartz/plugins/transformers/badges.ts +++ b/quartz/plugins/transformers/badges.ts @@ -98,7 +98,7 @@ const REGEXP = /\[!!([^\]]+)\]/gm const CODEREGEX = /`([^`\n]+)`/g export interface Options { - customBadges: Array, // Write in format [ [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA], [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA] ] + customBadges: Array<[string, string, [number, number, number, number], number]>, // Write in format [ [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA], [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA] ] } const defaultOptions: Options = { From 02b8c1db958dd474c710835a7d14d192a9c3c436 Mon Sep 17 00:00:00 2001 From: WinnerWind <71366717+WinnerWind@users.noreply.github.com> Date: Tue, 15 Jul 2025 12:43:05 +0530 Subject: [PATCH 04/13] Add an option to include CSS for proper rendering Now, you don't need an Obsidian Theme to use this plugin. --- quartz/plugins/transformers/badges.ts | 12 ++- quartz/styles/badges-optional.scss | 126 ++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 quartz/styles/badges-optional.scss diff --git a/quartz/plugins/transformers/badges.ts b/quartz/plugins/transformers/badges.ts index 789c2f459..82aab8cbc 100644 --- a/quartz/plugins/transformers/badges.ts +++ b/quartz/plugins/transformers/badges.ts @@ -1,5 +1,6 @@ import { QuartzTransformerPlugin } from "../types" import badgesCSS from "../../styles/badges.scss" +import obsidianOptionalCSS from "../../styles/badges-optional.scss" import { JSResource, CSSResource } from "../../util/resources" import icons from "lucide-static" @@ -99,10 +100,12 @@ const CODEREGEX = /`([^`\n]+)`/g export interface Options { customBadges: Array<[string, string, [number, number, number, number], number]>, // Write in format [ [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA], [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA] ] + useObsidianCSS: boolean } const defaultOptions: Options = { - customBadges: [] + customBadges: [], + useObsidianCSS: false, } export const InlineBadges: QuartzTransformerPlugin> = (userOpts) => { @@ -151,6 +154,13 @@ export const InlineBadges: QuartzTransformerPlugin> = (userOpts }`, inline: true, }) + + if (opts.useObsidianCSS){ + css.push({ + content: obsidianOptionalCSS, + inline: true + }) + } } return { js, css } }, diff --git a/quartz/styles/badges-optional.scss b/quartz/styles/badges-optional.scss new file mode 100644 index 000000000..d1ffef2a4 --- /dev/null +++ b/quartz/styles/badges-optional.scss @@ -0,0 +1,126 @@ +// This CSS is required to render badges properly. +// WinnerWind. +:root{ + body{ + --radius-l: 12px; + --radius-m: 8px; + --radius-s: 4px; + a.external .external-icon { + height:0px; + margin:0em; + display:none; + } + } +} +:root[saved-theme="dark"] { + --chalk-grey: #dddddd; + --color-accent: hsl(var(--accent-h), var(--accent-s), var(--accent-l)); + --color-accent-1: hsl( + calc(var(--accent-h) - 3), + calc(var(--accent-s) * 1.02), + calc(var(--accent-l) * 1.15) + ); + --color-accent-2: hsl( + calc(var(--accent-h) - 5), + calc(var(--accent-s) * 1.05), + calc(var(--accent-l) * 1.29) + ); + --color-accent-hsl: var(--accent-h), var(--accent-s), var(--accent-l); + --color-base-00: #090909; + --color-base-10: #1f1f1f; + --color-base-100: #c6c6c6; + --color-base-30: #1f1f1f; + --color-base-35: #383838; + --color-base-40: #1f1f1f; + --color-base-50: #808080; + --color-base-60: #5f5f5f; + --color-base-70: #bdbdbd; + --color-blue: #027aff; + --color-blue-rgb: 2, 122, 255; + --color-cyan: #53dfdd; + --color-cyan-rgb: 83, 223, 221; + --color-green: #44cf6e; + --color-green-rgb: 68, 207, 110; + --color-orange: #e9973f; + --color-orange-rgb: 233, 151, 63; + --color-pink: #fa99cd; + --color-purple: #a882ff; + --color-purple-rgb: 168, 130, 255; + --color-red: #fb464c; + --color-red-rgb: 251, 70, 76; + --color-yellow: #e0de71; + --h1-color: var(--chalk-grey); + --h2-color: var(--chalk-grey); + --h3-color: var(--chalk-grey); + --h4-color: var(--chalk-grey); + --h5-color: var(--chalk-grey); + --h6-color: var(--chalk-grey); + --interactive-accent: var(--color-accent); + --interactive-accent-hover: var(--color-accent-1); + --mono-rgb-100: 255, 255, 255; + --shadow-l: 0px 1.8px 7.3px rgba(0, 0, 0, 0.071), 0px 6.3px 24.7px rgba(0, 0, 0, 0.112), + 0px 30px 90px rgba(0, 0, 0, 0.2); + --shadow-s: 0px 1px 2px rgba(0, 0, 0, 0.121), 0px 3.4px 6.7px rgba(0, 0, 0, 0.179), + 0px 15px 30px rgba(0, 0, 0, 0.3); + --text-accent: var(--color-accent-1); + color-scheme: dark; + + .callout { + --callout-blend-mode: lighten; + } +} + +:root[saved-theme="light"] { + --chalk-grey: #686868; + --color-accent: hsl(var(--accent-h), var(--accent-s), var(--accent-l)); + --color-accent-1: hsl( + calc(var(--accent-h) - 1), + calc(var(--accent-s) * 1.01), + calc(var(--accent-l) * 1.075) + ); + --color-accent-2: hsl( + calc(var(--accent-h) - 3), + calc(var(--accent-s) * 1.02), + calc(var(--accent-l) * 1.15) + ); + --color-accent-hsl: var(--accent-h), var(--accent-s), var(--accent-l); + --color-base-00: #fff; + --color-base-10: #fafafa; + --color-base-100: #222; + --color-base-30: #e0e0e0; + --color-base-35: #d4d4d4; + --color-base-40: #bdbdbd; + --color-base-50: #ababab; + --color-base-60: #707070; + --color-base-70: #5a5a5a; + --color-blue: #086ddd; + --color-blue-rgb: 8, 109, 221; + --color-cyan: #00bfbc; + --color-cyan-rgb: 0, 191, 188; + --color-green: #08b94e; + --color-green-rgb: 8, 185, 78; + --color-orange: #ec7500; + --color-orange-rgb: 236, 117, 0; + --color-pink: #d53984; + --color-purple: #7852ee; + --color-purple-rgb: 120, 82, 238; + --color-red: #e93147; + --color-red-rgb: 233, 49, 71; + --color-yellow: #e0ac00; + --h1-color: var(--chalk-grey); + --h2-color: var(--chalk-grey); + --h3-color: var(--chalk-grey); + --h4-color: var(--chalk-grey); + --h5-color: var(--chalk-grey); + --h6-color: var(--chalk-grey); + --mono-rgb-100: 0, 0, 0; + --shadow-l: 0px 1.8px 7.3px rgba(0, 0, 0, 0.071), 0px 6.3px 24.7px rgba(0, 0, 0, 0.112), + 0px 30px 90px rgba(0, 0, 0, 0.2); + --shadow-s: 0px 1px 2px rgba(0, 0, 0, 0.028), 0px 3.4px 6.7px rgba(0, 0, 0, 0.042), + 0px 15px 30px rgba(0, 0, 0, 0.07); + color-scheme: light; + + .callout { + --callout-blend-mode: darken; + } +} From da13359e5a9a13d9c51fe08ebfec75e01e47365d Mon Sep 17 00:00:00 2001 From: WinnerWind <71366717+WinnerWind@users.noreply.github.com> Date: Tue, 15 Jul 2025 12:46:53 +0530 Subject: [PATCH 05/13] Fix code style --- quartz/plugins/transformers/badges.ts | 32 +++++++++++++-------------- quartz/styles/badges-optional.scss | 32 +++++++++++++++------------ 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/quartz/plugins/transformers/badges.ts b/quartz/plugins/transformers/badges.ts index 82aab8cbc..245948961 100644 --- a/quartz/plugins/transformers/badges.ts +++ b/quartz/plugins/transformers/badges.ts @@ -99,7 +99,7 @@ const REGEXP = /\[!!([^\]]+)\]/gm const CODEREGEX = /`([^`\n]+)`/g export interface Options { - customBadges: Array<[string, string, [number, number, number, number], number]>, // Write in format [ [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA], [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA] ] + customBadges: Array<[string, string, [number, number, number, number], number]> // Write in format [ [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA], [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA] ] useObsidianCSS: boolean } @@ -115,8 +115,8 @@ export const InlineBadges: QuartzTransformerPlugin> = (userOpts name: "InlineBadges", textTransform(_ctx, src) { // Append custom badges. - for (let badge of opts.customBadges){ - allBadges.push([badge[1],badge[1],badge[0]]) // Pushes it to the array in the format [icon,icon,name]. + for (let badge of opts.customBadges) { + allBadges.push([badge[1], badge[1], badge[0]]) // Pushes it to the array in the format [icon,icon,name]. } var srcReplacement: string = src //Start by assuming there are no badges. @@ -142,25 +142,25 @@ export const InlineBadges: QuartzTransformerPlugin> = (userOpts inline: true, }) // Sets the colour of custom badges. - for (let badgeDef of opts.customBadges){ - let badgeColor: string = `${badgeDef[2][0]},${badgeDef[2][1]},${badgeDef[2][2]}` - let textColor: string = `rgba(var(--badge-color),${badgeDef[3]})` - let badgeName: string = badgeDef[1] // Removes the "" from the name. + for (let badgeDef of opts.customBadges) { + let badgeColor: string = `${badgeDef[2][0]},${badgeDef[2][1]},${badgeDef[2][2]}` + let textColor: string = `rgba(var(--badge-color),${badgeDef[3]})` + let badgeName: string = badgeDef[1] // Removes the "" from the name. css.push({ - content: `.inline-badge[data-inline-badge=${badgeName}] { + content: `.inline-badge[data-inline-badge=${badgeName}] { --badge-color: ${badgeColor}; color: rgba(var(--badge-color), ${badgeDef[2][3]}); background-color: ${textColor}; }`, - inline: true, - }) - - if (opts.useObsidianCSS){ - css.push({ - content: obsidianOptionalCSS, - inline: true + inline: true, }) - } + + if (opts.useObsidianCSS) { + css.push({ + content: obsidianOptionalCSS, + inline: true, + }) + } } return { js, css } }, diff --git a/quartz/styles/badges-optional.scss b/quartz/styles/badges-optional.scss index d1ffef2a4..7c0d9d363 100644 --- a/quartz/styles/badges-optional.scss +++ b/quartz/styles/badges-optional.scss @@ -1,16 +1,16 @@ // This CSS is required to render badges properly. // WinnerWind. -:root{ - body{ - --radius-l: 12px; - --radius-m: 8px; - --radius-s: 4px; - a.external .external-icon { - height:0px; - margin:0em; - display:none; - } +:root { + body { + --radius-l: 12px; + --radius-m: 8px; + --radius-s: 4px; + a.external .external-icon { + height: 0px; + margin: 0em; + display: none; } + } } :root[saved-theme="dark"] { --chalk-grey: #dddddd; @@ -58,9 +58,11 @@ --interactive-accent: var(--color-accent); --interactive-accent-hover: var(--color-accent-1); --mono-rgb-100: 255, 255, 255; - --shadow-l: 0px 1.8px 7.3px rgba(0, 0, 0, 0.071), 0px 6.3px 24.7px rgba(0, 0, 0, 0.112), + --shadow-l: + 0px 1.8px 7.3px rgba(0, 0, 0, 0.071), 0px 6.3px 24.7px rgba(0, 0, 0, 0.112), 0px 30px 90px rgba(0, 0, 0, 0.2); - --shadow-s: 0px 1px 2px rgba(0, 0, 0, 0.121), 0px 3.4px 6.7px rgba(0, 0, 0, 0.179), + --shadow-s: + 0px 1px 2px rgba(0, 0, 0, 0.121), 0px 3.4px 6.7px rgba(0, 0, 0, 0.179), 0px 15px 30px rgba(0, 0, 0, 0.3); --text-accent: var(--color-accent-1); color-scheme: dark; @@ -114,9 +116,11 @@ --h5-color: var(--chalk-grey); --h6-color: var(--chalk-grey); --mono-rgb-100: 0, 0, 0; - --shadow-l: 0px 1.8px 7.3px rgba(0, 0, 0, 0.071), 0px 6.3px 24.7px rgba(0, 0, 0, 0.112), + --shadow-l: + 0px 1.8px 7.3px rgba(0, 0, 0, 0.071), 0px 6.3px 24.7px rgba(0, 0, 0, 0.112), 0px 30px 90px rgba(0, 0, 0, 0.2); - --shadow-s: 0px 1px 2px rgba(0, 0, 0, 0.028), 0px 3.4px 6.7px rgba(0, 0, 0, 0.042), + --shadow-s: + 0px 1px 2px rgba(0, 0, 0, 0.028), 0px 3.4px 6.7px rgba(0, 0, 0, 0.042), 0px 15px 30px rgba(0, 0, 0, 0.07); color-scheme: light; From b669be6e06809c68a0b75a34645e38aeec518af6 Mon Sep 17 00:00:00 2001 From: WinnerWind <71366717+WinnerWind@users.noreply.github.com> Date: Tue, 15 Jul 2025 16:23:55 +0530 Subject: [PATCH 06/13] Use a dictionary instead of an array. Should make it easier to define badges. --- quartz/plugins/transformers/badges.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/quartz/plugins/transformers/badges.ts b/quartz/plugins/transformers/badges.ts index 245948961..d631047ab 100644 --- a/quartz/plugins/transformers/badges.ts +++ b/quartz/plugins/transformers/badges.ts @@ -99,7 +99,12 @@ const REGEXP = /\[!!([^\]]+)\]/gm const CODEREGEX = /`([^`\n]+)`/g export interface Options { - customBadges: Array<[string, string, [number, number, number, number], number]> // Write in format [ [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA], [icon,name,[RED,GREEN,BLUE,ALPHA],TEXT_ALPHA] ] + customBadges: Array<{ + name: string + icon: string + color: [number, number, number, number] + textOpacity: number + }> useObsidianCSS: boolean } @@ -116,7 +121,7 @@ export const InlineBadges: QuartzTransformerPlugin> = (userOpts textTransform(_ctx, src) { // Append custom badges. for (let badge of opts.customBadges) { - allBadges.push([badge[1], badge[1], badge[0]]) // Pushes it to the array in the format [icon,icon,name]. + allBadges.push([badge.name, badge.name, badge.icon]) // Pushes it to the array in the format. } var srcReplacement: string = src //Start by assuming there are no badges. @@ -143,13 +148,13 @@ export const InlineBadges: QuartzTransformerPlugin> = (userOpts }) // Sets the colour of custom badges. for (let badgeDef of opts.customBadges) { - let badgeColor: string = `${badgeDef[2][0]},${badgeDef[2][1]},${badgeDef[2][2]}` - let textColor: string = `rgba(var(--badge-color),${badgeDef[3]})` - let badgeName: string = badgeDef[1] // Removes the "" from the name. + let badgeColor: string = `${badgeDef.color[0]},${badgeDef.color[1]},${badgeDef.color[2]}` + let textColor: string = `rgba(var(--badge-color),${badgeDef.textOpacity})` + let badgeName: string = badgeDef.name css.push({ content: `.inline-badge[data-inline-badge=${badgeName}] { --badge-color: ${badgeColor}; - color: rgba(var(--badge-color), ${badgeDef[2][3]}); + color: rgba(var(--badge-color), ${badgeDef.color[3]}); background-color: ${textColor}; }`, inline: true, From daabf148e8dd95a332d4cd796113ab96c0b20d7e Mon Sep 17 00:00:00 2001 From: WinnerWind <71366717+WinnerWind@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:08:36 +0530 Subject: [PATCH 07/13] Remove the need for Obsidian Optional CSS this CSS is now integrated with the normal css file, which uses quartz colours by default. --- quartz/plugins/transformers/badges.ts | 10 -- quartz/styles/badges-optional.scss | 130 -------------------------- quartz/styles/badges.scss | 33 ++++++- 3 files changed, 32 insertions(+), 141 deletions(-) delete mode 100644 quartz/styles/badges-optional.scss diff --git a/quartz/plugins/transformers/badges.ts b/quartz/plugins/transformers/badges.ts index d631047ab..2f7d85620 100644 --- a/quartz/plugins/transformers/badges.ts +++ b/quartz/plugins/transformers/badges.ts @@ -1,6 +1,5 @@ import { QuartzTransformerPlugin } from "../types" import badgesCSS from "../../styles/badges.scss" -import obsidianOptionalCSS from "../../styles/badges-optional.scss" import { JSResource, CSSResource } from "../../util/resources" import icons from "lucide-static" @@ -105,12 +104,10 @@ export interface Options { color: [number, number, number, number] textOpacity: number }> - useObsidianCSS: boolean } const defaultOptions: Options = { customBadges: [], - useObsidianCSS: false, } export const InlineBadges: QuartzTransformerPlugin> = (userOpts) => { @@ -159,13 +156,6 @@ export const InlineBadges: QuartzTransformerPlugin> = (userOpts }`, inline: true, }) - - if (opts.useObsidianCSS) { - css.push({ - content: obsidianOptionalCSS, - inline: true, - }) - } } return { js, css } }, diff --git a/quartz/styles/badges-optional.scss b/quartz/styles/badges-optional.scss deleted file mode 100644 index 7c0d9d363..000000000 --- a/quartz/styles/badges-optional.scss +++ /dev/null @@ -1,130 +0,0 @@ -// This CSS is required to render badges properly. -// WinnerWind. -:root { - body { - --radius-l: 12px; - --radius-m: 8px; - --radius-s: 4px; - a.external .external-icon { - height: 0px; - margin: 0em; - display: none; - } - } -} -:root[saved-theme="dark"] { - --chalk-grey: #dddddd; - --color-accent: hsl(var(--accent-h), var(--accent-s), var(--accent-l)); - --color-accent-1: hsl( - calc(var(--accent-h) - 3), - calc(var(--accent-s) * 1.02), - calc(var(--accent-l) * 1.15) - ); - --color-accent-2: hsl( - calc(var(--accent-h) - 5), - calc(var(--accent-s) * 1.05), - calc(var(--accent-l) * 1.29) - ); - --color-accent-hsl: var(--accent-h), var(--accent-s), var(--accent-l); - --color-base-00: #090909; - --color-base-10: #1f1f1f; - --color-base-100: #c6c6c6; - --color-base-30: #1f1f1f; - --color-base-35: #383838; - --color-base-40: #1f1f1f; - --color-base-50: #808080; - --color-base-60: #5f5f5f; - --color-base-70: #bdbdbd; - --color-blue: #027aff; - --color-blue-rgb: 2, 122, 255; - --color-cyan: #53dfdd; - --color-cyan-rgb: 83, 223, 221; - --color-green: #44cf6e; - --color-green-rgb: 68, 207, 110; - --color-orange: #e9973f; - --color-orange-rgb: 233, 151, 63; - --color-pink: #fa99cd; - --color-purple: #a882ff; - --color-purple-rgb: 168, 130, 255; - --color-red: #fb464c; - --color-red-rgb: 251, 70, 76; - --color-yellow: #e0de71; - --h1-color: var(--chalk-grey); - --h2-color: var(--chalk-grey); - --h3-color: var(--chalk-grey); - --h4-color: var(--chalk-grey); - --h5-color: var(--chalk-grey); - --h6-color: var(--chalk-grey); - --interactive-accent: var(--color-accent); - --interactive-accent-hover: var(--color-accent-1); - --mono-rgb-100: 255, 255, 255; - --shadow-l: - 0px 1.8px 7.3px rgba(0, 0, 0, 0.071), 0px 6.3px 24.7px rgba(0, 0, 0, 0.112), - 0px 30px 90px rgba(0, 0, 0, 0.2); - --shadow-s: - 0px 1px 2px rgba(0, 0, 0, 0.121), 0px 3.4px 6.7px rgba(0, 0, 0, 0.179), - 0px 15px 30px rgba(0, 0, 0, 0.3); - --text-accent: var(--color-accent-1); - color-scheme: dark; - - .callout { - --callout-blend-mode: lighten; - } -} - -:root[saved-theme="light"] { - --chalk-grey: #686868; - --color-accent: hsl(var(--accent-h), var(--accent-s), var(--accent-l)); - --color-accent-1: hsl( - calc(var(--accent-h) - 1), - calc(var(--accent-s) * 1.01), - calc(var(--accent-l) * 1.075) - ); - --color-accent-2: hsl( - calc(var(--accent-h) - 3), - calc(var(--accent-s) * 1.02), - calc(var(--accent-l) * 1.15) - ); - --color-accent-hsl: var(--accent-h), var(--accent-s), var(--accent-l); - --color-base-00: #fff; - --color-base-10: #fafafa; - --color-base-100: #222; - --color-base-30: #e0e0e0; - --color-base-35: #d4d4d4; - --color-base-40: #bdbdbd; - --color-base-50: #ababab; - --color-base-60: #707070; - --color-base-70: #5a5a5a; - --color-blue: #086ddd; - --color-blue-rgb: 8, 109, 221; - --color-cyan: #00bfbc; - --color-cyan-rgb: 0, 191, 188; - --color-green: #08b94e; - --color-green-rgb: 8, 185, 78; - --color-orange: #ec7500; - --color-orange-rgb: 236, 117, 0; - --color-pink: #d53984; - --color-purple: #7852ee; - --color-purple-rgb: 120, 82, 238; - --color-red: #e93147; - --color-red-rgb: 233, 49, 71; - --color-yellow: #e0ac00; - --h1-color: var(--chalk-grey); - --h2-color: var(--chalk-grey); - --h3-color: var(--chalk-grey); - --h4-color: var(--chalk-grey); - --h5-color: var(--chalk-grey); - --h6-color: var(--chalk-grey); - --mono-rgb-100: 0, 0, 0; - --shadow-l: - 0px 1.8px 7.3px rgba(0, 0, 0, 0.071), 0px 6.3px 24.7px rgba(0, 0, 0, 0.112), - 0px 30px 90px rgba(0, 0, 0, 0.2); - --shadow-s: - 0px 1px 2px rgba(0, 0, 0, 0.028), 0px 3.4px 6.7px rgba(0, 0, 0, 0.042), - 0px 15px 30px rgba(0, 0, 0, 0.07); - color-scheme: light; - - .callout { - --callout-blend-mode: darken; - } -} diff --git a/quartz/styles/badges.scss b/quartz/styles/badges.scss index 54d03e9f6..19f233b54 100644 --- a/quartz/styles/badges.scss +++ b/quartz/styles/badges.scss @@ -1,9 +1,40 @@ +:root { + body { + --radius-l: 12px; + --radius-m: 8px; + --radius-s: 4px; + a.external .external-icon { + height: 0px; + margin: 0em; + display: none; + } + } +} + +:root { + --color-blue: #448aff; + --color-blue-rgb: 68, 138, 256; + --color-cyan: #00b0ff; + --color-cyan-rgb: 00, 176, 256; + --color-green: #09ad7a; + --color-green-rgb: 9, 173, 122; + --color-orange: #db8942; + --color-orange-rgb: 219, 137, 66; + --color-pink: #fa99cd; + --color-pink-rgb: 250, 153, 205; + --color-purple: #7a43b5; + --color-purple-rgb: 122, 67, 181; + --color-red: #db4242; + --color-red-rgb: 219, 66, 66; + --color-yellow: #dba642; + --color-yellow-rgb: 219, 166, 66; +} + body { --inline-badge-border-color: transparent; --inline-badge-font-size: 0.8em; --inline-badge-border-radius: var(--radius-s); --inline-badge-border: 1px solid var(--inline-badge-border-color); - --my-custom-rgb: var(--color-green-rgb); } .inline-badge { display: inline-flex; From 889217453c1e733636cc108f923153d377cd08a4 Mon Sep 17 00:00:00 2001 From: WinnerWind <71366717+WinnerWind@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:09:56 +0530 Subject: [PATCH 08/13] Move SVG alignment code to `badges.scss` --- quartz/plugins/transformers/badges.ts | 5 ----- quartz/styles/badges.scss | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/quartz/plugins/transformers/badges.ts b/quartz/plugins/transformers/badges.ts index 2f7d85620..765e4a08e 100644 --- a/quartz/plugins/transformers/badges.ts +++ b/quartz/plugins/transformers/badges.ts @@ -138,11 +138,6 @@ export const InlineBadges: QuartzTransformerPlugin> = (userOpts content: badgesCSS, inline: true, }) - css.push({ - // Requirement for the SVG to align properly. - content: ".inline-badge .inline-badge-icon svg {display: flex;align-self: center;}", - inline: true, - }) // Sets the colour of custom badges. for (let badgeDef of opts.customBadges) { let badgeColor: string = `${badgeDef.color[0]},${badgeDef.color[1]},${badgeDef.color[2]}` diff --git a/quartz/styles/badges.scss b/quartz/styles/badges.scss index 19f233b54..de0c960c9 100644 --- a/quartz/styles/badges.scss +++ b/quartz/styles/badges.scss @@ -69,7 +69,10 @@ body { .inline-badge .inline-badge-icon svg { width: 14px; height: 14px; + display: flex; + align-self: center; } + .inline-badge .inline-badge-extra, .inline-badge .inline-badge-icon { display: inline-flex !important; From 1d38be71acb4eed6a0521b74c1855d349436fcfe Mon Sep 17 00:00:00 2001 From: WinnerWind <71366717+WinnerWind@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:52:41 +0530 Subject: [PATCH 09/13] Add documentation --- docs/plugins/InlineBadges.md | 79 ++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 docs/plugins/InlineBadges.md diff --git a/docs/plugins/InlineBadges.md b/docs/plugins/InlineBadges.md new file mode 100644 index 000000000..4cc481506 --- /dev/null +++ b/docs/plugins/InlineBadges.md @@ -0,0 +1,79 @@ +--- +title: InlineBadges +tags: + - plugin/transformer +--- + +This plugin allows the user to create multipurpose badges that can be used within inline text. + +> [!note] +> For information on how to add, remove or configure plugins, see the [[configuration#Plugins|Configuration]] page. + +## Syntax + +A badge may be used in line with the following syntax, within an inline code block: + +```md +`[!!KEY:TEXT]` -> Normal badges +`[!!|ghs>SUBTEXT:TEXT]` -> Github Success +`[!!|ghb>SUBTEXT:TEXT]` -> Github Blue +`[!!|KEY:TEXT]` -> Plaintext +`[!!|ICON|ARIA:TEXT|COLOR-RGB]` -> Custom icon. Color can be a CSS Variable, eg. `var(--color-red-rgb)` +``` + +- `KEY` defines the name of the preset to be used, which are equal to the [obsidian callout types](https://help.obsidian.md/callouts#Supported+types). +- `TEXT` and `SUBTEXT` can be any text of your choosing. They cannot contain the `|` character as it is the main delimiter. +- `ICON` is the name of the [lucide icon](https://lucide.dev/) to be used.[^1] +- `ARIA` defines the name of the aria label used for the badge (Not visible to the end user). +- `COLOR-RGB` is a HTML color, such as `256, 12, 32`. +- `ghs` and `ghb` should not be changed, as they define what colour is to be used. + +### Examples: + +```md +`[!!note:There is no such thing as genuine questions]` +`[!!|ghs>checks:success]` +`[!!|ghb>contributors:52]` +`[!!|warning:Don't say that I didn't tell you.]` +`[!!|menu|menu-item:Go to Settings > Delete Account|140, 200, 110]` +``` + +```md +What is `[!!danger:love]`? +``` + +## Configuration + +This plugin accepts the following configuration options: + +- `customBadges`, an array of objects that defines custom badge types, along with their colours and icon. + +### Badge Object + +A badge object is defined like so: + +```ts +{ + name: "foobar", + icon: "icon_name", + color: [10, 10, 10, 1], + textOpacity: 0.117, +} +``` + +Where `foobar` is the name of the badge, `icon` is the name of the [lucide icon](https://lucide.dev/)[^1] to be used, `color` is an array defining the background colour in the format `[RED, GREEN, BLUE, ALPHA]`[^2] +These custom badges can now be used with the syntax + +```md +`[!!foobar:text-here]` +``` + +## API + +- Category: Transformer +- Function name: `Plugin.InlineBadges()` +- Source: N/A + +[^1]: The name must be in lower case. That is, use `accessibility` instead of `Accessibility`, `lucide-accessibility` or `LiAccessibility` (The plugin may not accept them!) + +[^2]: Note that `ALPHA` only goes from `0` to `1`, whereas the rest go from `0` to `256` From d45b010ce2bb8b8bb74e02b032fd8d9209f5e82b Mon Sep 17 00:00:00 2001 From: WinnerWind <71366717+WinnerWind@users.noreply.github.com> Date: Wed, 23 Jul 2025 19:08:34 +0530 Subject: [PATCH 10/13] Block multi-line code blocks from being caught This prevents multi-line code blocks containing a badge definition to be replaced with badge HTML which is certainly unintended. --- quartz/plugins/transformers/badges.ts | 34 ++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/quartz/plugins/transformers/badges.ts b/quartz/plugins/transformers/badges.ts index 765e4a08e..bf1f470fe 100644 --- a/quartz/plugins/transformers/badges.ts +++ b/quartz/plugins/transformers/badges.ts @@ -94,8 +94,11 @@ export const BADGE_TYPES: any[] = [ var allBadges: any[] = BADGE_TYPES //Append custom badges to the end of this array. -const REGEXP = /\[!!([^\]]+)\]/gm -const CODEREGEX = /`([^`\n]+)`/g +// Catches all badge blocks with syntax `[!!...]` but not `[!!] +const REGEXP = /\`\[!!([^\]]+)\]\`/gm + +// Catches all multiline code blocks +const FENCED_CODE_REGEX = /(```[\s\S]*?```|~~~[\s\S]*?~~~)/gm export interface Options { customBadges: Array<{ @@ -121,12 +124,31 @@ export const InlineBadges: QuartzTransformerPlugin> = (userOpts allBadges.push([badge.name, badge.name, badge.icon]) // Pushes it to the array in the format. } - var srcReplacement: string = src //Start by assuming there are no badges. - for (const match of src.matchAll(CODEREGEX)) { - for (const badgeMatch of match[0].matchAll(REGEXP)) { - srcReplacement = srcReplacement.replace(match[0], buildBadge(badgeMatch[0])) + // Required later to ensure we don't change badges that are within multiline code blocks + const fencedCodeBlocks: Array<[number, number]> = [] + for (const match of src.matchAll(FENCED_CODE_REGEX)) { + fencedCodeBlocks.push([match.index!, match.index! + match[0].length]) + } + // Helper to check if a given index is inside any code block + function isInFencedBlock(idx: number) { + return fencedCodeBlocks.some(([start, end]) => idx >= start && idx < end) + } + + let lastIndex = 0 //Last code block that was checked + let srcReplacement = "" //Build the source text from this. + + for (const match of src.matchAll(REGEXP)) { + const matchIndex = match.index! + const matchEnd = matchIndex + match[0].length + + if (!isInFencedBlock(matchIndex)) { + srcReplacement += src.slice(lastIndex, matchIndex) + srcReplacement += buildBadge(match[0].replaceAll("`", "")) //Get rid of trailing code block syntax. + lastIndex = matchEnd } } + + srcReplacement += src.slice(lastIndex) return srcReplacement }, externalResources() { From 901bb246fe257d51992c1841d6d7c6bebee42ec0 Mon Sep 17 00:00:00 2001 From: "S. Dash." <71366717+WinnerWind@users.noreply.github.com> Date: Sun, 10 Aug 2025 11:30:37 +0530 Subject: [PATCH 11/13] Fix tab spacing in documentation Co-authored-by: Emile Bangma --- docs/plugins/InlineBadges.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/plugins/InlineBadges.md b/docs/plugins/InlineBadges.md index 4cc481506..8d9e1adc2 100644 --- a/docs/plugins/InlineBadges.md +++ b/docs/plugins/InlineBadges.md @@ -54,10 +54,10 @@ A badge object is defined like so: ```ts { - name: "foobar", - icon: "icon_name", - color: [10, 10, 10, 1], - textOpacity: 0.117, + name: "foobar", + icon: "icon_name", + color: [10, 10, 10, 1], + textOpacity: 0.117, } ``` From e7cd2487578e3c1c55c6b4986cb85848a570d5fd Mon Sep 17 00:00:00 2001 From: WinnerWind <71366717+WinnerWind@users.noreply.github.com> Date: Sun, 10 Aug 2025 11:36:58 +0530 Subject: [PATCH 12/13] Define the type for BADGE_TYPES and allBadges --- quartz/plugins/transformers/badges.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quartz/plugins/transformers/badges.ts b/quartz/plugins/transformers/badges.ts index bf1f470fe..7b5983448 100644 --- a/quartz/plugins/transformers/badges.ts +++ b/quartz/plugins/transformers/badges.ts @@ -3,7 +3,7 @@ import badgesCSS from "../../styles/badges.scss" import { JSResource, CSSResource } from "../../util/resources" import icons from "lucide-static" -export const BADGE_TYPES: any[] = [ +export const BADGE_TYPES: [string, string, string][] = [ ["note", "Note", "lucide-pencil"], ["info", "Info", "lucide-info"], ["todo", "Todo", "lucide-check-circle-2"], @@ -92,7 +92,7 @@ export const BADGE_TYPES: any[] = [ ["vault", "Vault", "vault"], ] -var allBadges: any[] = BADGE_TYPES //Append custom badges to the end of this array. +var allBadges: [string, string, string][] = BADGE_TYPES //Append custom badges to the end of this array. // Catches all badge blocks with syntax `[!!...]` but not `[!!] const REGEXP = /\`\[!!([^\]]+)\]\`/gm From 47e64b04a1e8eb0bba82bfec7c6bae1b5805dcd1 Mon Sep 17 00:00:00 2001 From: WinnerWind <71366717+WinnerWind@users.noreply.github.com> Date: Sun, 10 Aug 2025 11:52:21 +0530 Subject: [PATCH 13/13] Remove trailing comma from package.json This prevented checks from passing. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 03d7ad0a4..ea04389d8 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,6 @@ "prettier": "^3.6.2", "tsx": "^4.20.3", "typescript": "^5.8.3", - "lucide-static": "0.511.0", + "lucide-static": "0.511.0" } }