From 8be5be706d19ca34fa8e342327b78e11aff3d4f0 Mon Sep 17 00:00:00 2001 From: saberzero1 Date: Wed, 10 Jun 2026 23:13:02 +0200 Subject: [PATCH] chore: fonts --- README.md | 36 +++++++++++++-------------- dist/emitter.d.ts | 6 ++--- dist/emitter.js | 12 ++++----- dist/emitter.js.map | 2 +- dist/index.d.ts | 8 +++--- dist/index.js | 20 +++++++-------- dist/index.js.map | 2 +- dist/types.d.ts | 4 +-- package.json | 6 ++--- src/emitter.ts | 18 +++++++------- src/index.ts | 6 ++--- src/transformer.ts | 26 +++++++++---------- src/types.ts | 2 +- test/transformer.test.ts | 54 ++++++++++++++++++++-------------------- 14 files changed, 101 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index d98fbfd..b22307c 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ -# @quartz-community/quartz-fonts +# @quartz-community/fonts Fine-grained font control for Quartz sites. Supports per-heading fonts, automatic theme font discovery via [QuartzTheme](https://github.com/saberzero1/quartz-themes), and Obsidian-compatible defaults. ## Installation ```bash -npx quartz plugin add github:quartz-community/quartz-fonts +npx quartz plugin add github:quartz-community/fonts ``` ## Usage ```yaml title="quartz.config.yaml" plugins: - - source: github:quartz-community/quartz-fonts + - source: github:quartz-community/fonts enabled: true ``` @@ -20,7 +20,7 @@ With custom fonts: ```yaml title="quartz.config.yaml" plugins: - - source: github:quartz-community/quartz-fonts + - source: github:quartz-community/fonts enabled: true options: body: '"Inter", sans-serif' @@ -62,7 +62,7 @@ body: ### Default options ```yaml title="quartz.config.yaml" -- source: github:quartz-community/quartz-fonts +- source: github:quartz-community/fonts enabled: true options: useThemeFonts: true @@ -71,7 +71,7 @@ body: ## How it works -QuartzFonts resolves fonts using a priority chain: +Fonts resolves fonts using a priority chain: ``` User config (plugin options) @@ -93,29 +93,29 @@ title option -> header option -> theme font -> Obsidian default ### With QuartzTheme -When [QuartzTheme](https://github.com/saberzero1/quartz-themes) is installed and runs before QuartzFonts, theme fonts are automatically discovered and used as defaults. Any options you set in QuartzFonts will override the theme fonts. +When [QuartzTheme](https://github.com/saberzero1/quartz-themes) is installed and runs before Fonts, theme fonts are automatically discovered and used as defaults. Any options you set in Fonts will override the theme fonts. -QuartzFonts must run after QuartzTheme. This is handled automatically by `defaultOrder` (QuartzTheme = 50, QuartzFonts = 60). +Fonts must run after QuartzTheme. This is handled automatically by `defaultOrder` (QuartzTheme = 50, Fonts = 60). ### Without QuartzTheme -QuartzFonts works standalone. Without a theme, it falls back to Obsidian's default system font stacks. +Fonts works standalone. Without a theme, it falls back to Obsidian's default system font stacks. ## Examples ```yaml title="quartz.config.yaml" # Use theme fonts automatically (default behavior) -- source: github:quartz-community/quartz-fonts +- source: github:quartz-community/fonts enabled: true # Override just the heading font -- source: github:quartz-community/quartz-fonts +- source: github:quartz-community/fonts enabled: true options: header: '"Playfair Display", serif' # Full control with per-heading fonts -- source: github:quartz-community/quartz-fonts +- source: github:quartz-community/fonts enabled: true options: body: '"Inter", sans-serif' @@ -125,7 +125,7 @@ QuartzFonts works standalone. Without a theme, it falls back to Obsidian's defau h2: '"Lora", serif' # Load from Google Fonts automatically -- source: github:quartz-community/quartz-fonts +- source: github:quartz-community/fonts enabled: true options: fontOrigin: googleFonts @@ -134,7 +134,7 @@ QuartzFonts works standalone. Without a theme, it falls back to Obsidian's defau code: JetBrains Mono # Google Fonts with weight/italic control -- source: github:quartz-community/quartz-fonts +- source: github:quartz-community/fonts enabled: true options: fontOrigin: googleFonts @@ -150,7 +150,7 @@ QuartzFonts works standalone. Without a theme, it falls back to Obsidian's defau weights: [400] # Custom title font (separate from header) -- source: github:quartz-community/quartz-fonts +- source: github:quartz-community/fonts enabled: true options: fontOrigin: googleFonts @@ -160,7 +160,7 @@ QuartzFonts works standalone. Without a theme, it falls back to Obsidian's defau code: JetBrains Mono # Ignore theme fonts entirely -- source: github:quartz-community/quartz-fonts +- source: github:quartz-community/fonts enabled: true options: useThemeFonts: false @@ -169,7 +169,7 @@ QuartzFonts works standalone. Without a theme, it falls back to Obsidian's defau ## Google Fonts Validation -When `fontOrigin: googleFonts` is set and the optional [`google-font-metadata`](https://www.npmjs.com/package/google-font-metadata) package is installed, QuartzFonts validates your font configuration at build time: +When `fontOrigin: googleFonts` is set and the optional [`google-font-metadata`](https://www.npmjs.com/package/google-font-metadata) package is installed, Fonts validates your font configuration at build time: - Checks that font family names exist in Google Fonts. - Warns if requested weights are not available for a font. @@ -185,7 +185,7 @@ Validation warnings are logged to the console but do not block the build. ## Documentation -See the [Quartz documentation](https://quartz.jzhao.xyz/plugins/QuartzFonts) for more information. +See the [Quartz documentation](https://quartz.jzhao.xyz/plugins/Fonts) for more information. ## License diff --git a/dist/emitter.d.ts b/dist/emitter.d.ts index 3d70e1e..eb0a596 100644 --- a/dist/emitter.d.ts +++ b/dist/emitter.d.ts @@ -1,6 +1,6 @@ import { QuartzEmitterPlugin } from '@quartz-community/types'; -import { QuartzFontsOptions } from './types.js'; +import { FontsOptions } from './types.js'; -declare const QuartzFontsEmitter: QuartzEmitterPlugin>; +declare const FontsEmitter: QuartzEmitterPlugin>; -export { QuartzFontsEmitter }; +export { FontsEmitter }; diff --git a/dist/emitter.js b/dist/emitter.js index 7ff91f8..ee64475 100644 --- a/dist/emitter.js +++ b/dist/emitter.js @@ -92,17 +92,17 @@ var defaultOptions = { useThemeFonts: true, fontOrigin: "googleFonts" }; -var QuartzFontsEmitter = (userOptions) => { +var FontsEmitter = (userOptions) => { const options = { ...defaultOptions, ...userOptions }; return { - name: "QuartzFontsEmitter", + name: "FontsEmitter", async *emit(ctx, _content, _resources) { if (options.fontOrigin !== "selfHosted") { return; } const baseUrl = ctx.cfg.configuration.baseUrl; if (!baseUrl) { - throw new Error("[QuartzFontsEmitter] baseUrl is required for selfHosted fonts."); + throw new Error("[FontsEmitter] baseUrl is required for selfHosted fonts."); } const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER; const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY; @@ -116,7 +116,7 @@ var QuartzFontsEmitter = (userOptions) => { const cssResponse = await fetch(href); if (!cssResponse.ok) { throw new Error( - `[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}` + `[FontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}` ); } const cssText = await cssResponse.text(); @@ -127,7 +127,7 @@ var QuartzFontsEmitter = (userOptions) => { const fontResponse = await fetch(fontFile.url); if (!fontResponse.ok) { throw new Error( - `[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})` + `[FontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})` ); } const buf = await fontResponse.arrayBuffer(); @@ -142,6 +142,6 @@ var QuartzFontsEmitter = (userOptions) => { }; }; -export { QuartzFontsEmitter }; +export { FontsEmitter }; //# sourceMappingURL=emitter.js.map //# sourceMappingURL=emitter.js.map \ No newline at end of file diff --git a/dist/emitter.js.map b/dist/emitter.js.map index 308d6ec..0bc9225 100644 --- a/dist/emitter.js.map +++ b/dist/emitter.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/defaults.ts","../src/util/google-fonts.ts","../src/util/process-fonts.ts","../src/emitter.ts"],"names":[],"mappings":"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","file":"emitter.js","sourcesContent":["export const OBSIDIAN_SANS_STACK =\n 'ui-sans-serif, -apple-system, BlinkMacSystemFont, system-ui, \"Segoe UI\", Roboto, \"Inter\", \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", sans-serif';\n\nexport const OBSIDIAN_MONO_STACK =\n 'ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Roboto Mono\", \"DejaVu Sans Mono\", \"Liberation Mono\", Menlo, Monaco, \"Consolas\", \"Source Code Pro\", monospace';\n\nexport type FontRole = \"title\" | \"header\" | \"body\" | \"code\";\n\nexport const DEFAULT_WEIGHTS: Record = {\n title: [400, 700],\n header: [400, 700],\n body: [400, 600],\n code: [400, 600],\n};\n\nexport const DEFAULT_ITALIC: Record = {\n title: false,\n header: false,\n body: true,\n code: false,\n};\n","import type { FontSpecification } from \"../types\";\nimport type { FontRole } from \"../defaults\";\nimport { DEFAULT_WEIGHTS, DEFAULT_ITALIC } from \"../defaults\";\n\nfunction normalizeFontSpec(spec: FontSpecification): {\n name: string;\n weights?: number[];\n includeItalic?: boolean;\n} {\n return typeof spec === \"string\" ? { name: spec } : spec;\n}\n\nexport function getFontName(spec: FontSpecification): string {\n return typeof spec === \"string\" ? spec : spec.name;\n}\n\nexport function formatFontSpecification(role: FontRole, spec: FontSpecification): string {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const features: string[] = [];\n if (italic) {\n features.push(\"ital\");\n }\n\n if (resolvedWeights.length > 1) {\n const weightSpec = italic\n ? resolvedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : resolvedWeights.join(\";\");\n\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${name}:${features.join(\",\")}`;\n }\n\n return name;\n}\n\ninterface MergedFontEntry {\n name: string;\n weights: Set;\n italic: boolean;\n}\n\nfunction mergeInto(\n map: Map,\n role: FontRole,\n spec: FontSpecification,\n): void {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const existing = map.get(name);\n if (existing) {\n for (const w of resolvedWeights) existing.weights.add(w);\n if (italic) existing.italic = true;\n } else {\n map.set(name, { name, weights: new Set(resolvedWeights), italic });\n }\n}\n\nfunction formatMergedEntry(entry: MergedFontEntry): string {\n const sortedWeights = [...entry.weights].sort((a, b) => a - b);\n const features: string[] = [];\n\n if (entry.italic) {\n features.push(\"ital\");\n }\n\n if (sortedWeights.length > 1) {\n const weightSpec = entry.italic\n ? sortedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : sortedWeights.join(\";\");\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${entry.name}:${features.join(\",\")}`;\n }\n return entry.name;\n}\n\nexport function googleFontHref(fonts: {\n title?: FontSpecification;\n header: FontSpecification;\n body: FontSpecification;\n code: FontSpecification;\n}): string {\n const merged = new Map();\n\n mergeInto(merged, \"header\", fonts.header);\n mergeInto(merged, \"body\", fonts.body);\n mergeInto(merged, \"code\", fonts.code);\n\n if (fonts.title) {\n mergeInto(merged, \"title\", fonts.title);\n }\n\n const families = [...merged.values()].map(formatMergedEntry);\n const params = families.map((f) => `family=${encodeURIComponent(f)}`).join(\"&\");\n return `https://fonts.googleapis.com/css2?${params}&display=swap`;\n}\n","import type { ProcessedFontResult, GoogleFontFile } from \"../types\";\n\nconst fontMimeMap: Record = {\n truetype: \"ttf\",\n woff: \"woff\",\n woff2: \"woff2\",\n opentype: \"otf\",\n};\n\nconst fontUrlPattern =\n /url\\((https:\\/\\/fonts.gstatic.com\\/.+(?:\\/|(?:kit=))(.+?)[.&].+?)\\)\\sformat\\('(\\w+?)'\\);/g;\n\nexport function processGoogleFonts(stylesheet: string, baseUrl: string): ProcessedFontResult {\n const fontFiles: GoogleFontFile[] = [];\n\n const processedStylesheet = stylesheet.replace(\n fontUrlPattern,\n (match, url: string, filename: string, format: string) => {\n const extension = fontMimeMap[format] ?? format;\n const rewrittenUrl = `https://${baseUrl}/static/fonts/${filename}.${extension}`;\n fontFiles.push({ url, filename, extension });\n return match.replace(url, rewrittenUrl);\n },\n );\n\n return { processedStylesheet, fontFiles };\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport type { QuartzEmitterPlugin, BuildCtx, FilePath } from \"@quartz-community/types\";\nimport type { QuartzFontsOptions } from \"./types\";\nimport { googleFontHref } from \"./util/google-fonts\";\nimport { processGoogleFonts } from \"./util/process-fonts\";\n\nconst QUARTZ_DEFAULT_HEADER = \"Schibsted Grotesk\";\nconst QUARTZ_DEFAULT_BODY = \"Source Sans Pro\";\nconst QUARTZ_DEFAULT_CODE = \"IBM Plex Mono\";\n\nconst defaultOptions: QuartzFontsOptions = {\n useThemeFonts: true,\n fontOrigin: \"googleFonts\",\n};\n\nexport const QuartzFontsEmitter: QuartzEmitterPlugin> = (\n userOptions?: Partial,\n) => {\n const options: QuartzFontsOptions = { ...defaultOptions, ...userOptions };\n\n return {\n name: \"QuartzFontsEmitter\",\n async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator {\n if (options.fontOrigin !== \"selfHosted\") {\n return;\n }\n\n const baseUrl = ctx.cfg.configuration.baseUrl;\n if (!baseUrl) {\n throw new Error(\"[QuartzFontsEmitter] baseUrl is required for selfHosted fonts.\");\n }\n\n const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;\n const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;\n const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;\n\n const href = googleFontHref({\n title: options.title,\n header: headerSpec,\n body: bodySpec,\n code: codeSpec,\n });\n\n const cssResponse = await fetch(href);\n if (!cssResponse.ok) {\n throw new Error(\n `[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`,\n );\n }\n const cssText = await cssResponse.text();\n\n const { processedStylesheet, fontFiles } = processGoogleFonts(cssText, baseUrl);\n\n const fontsDir = path.join(ctx.argv.output, \"static\", \"fonts\");\n await fs.promises.mkdir(fontsDir, { recursive: true });\n\n for (const fontFile of fontFiles) {\n const fontResponse = await fetch(fontFile.url);\n if (!fontResponse.ok) {\n throw new Error(\n `[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`,\n );\n }\n const buf = await fontResponse.arrayBuffer();\n const filePath = path.join(fontsDir, `${fontFile.filename}.${fontFile.extension}`);\n await fs.promises.writeFile(filePath, Buffer.from(buf));\n yield filePath as FilePath;\n }\n\n const cssPath = path.join(fontsDir, \"quartz-fonts.css\");\n await fs.promises.writeFile(cssPath, processedStylesheet);\n yield cssPath as FilePath;\n },\n };\n};\n"]} \ No newline at end of file +{"version":3,"sources":["../src/defaults.ts","../src/util/google-fonts.ts","../src/util/process-fonts.ts","../src/emitter.ts"],"names":[],"mappings":";;;;;;;AAQO,IAAM,eAAA,GAA8C;AAAA,EACzD,KAAA,EAAO,CAAC,GAAA,EAAK,GAAG,CAAA;AAAA,EAChB,MAAA,EAAQ,CAAC,GAAA,EAAK,GAAG,CAAA;AAAA,EACjB,IAAA,EAAM,CAAC,GAAA,EAAK,GAAG,CAAA;AAAA,EACf,IAAA,EAAM,CAAC,GAAA,EAAK,GAAG;AACjB,CAAA;AAEO,IAAM,cAAA,GAA4C;AAAA,EACvD,KAAA,EAAO,KAAA;AAAA,EACP,MAAA,EAAQ,KAAA;AAAA,EACR,IAAA,EAAM,IAAA;AAAA,EACN,IAAA,EAAM;AACR,CAAA;;;AChBA,SAAS,kBAAkB,IAAA,EAIzB;AACA,EAAA,OAAO,OAAO,IAAA,KAAS,QAAA,GAAW,EAAE,IAAA,EAAM,MAAK,GAAI,IAAA;AACrD;AAyCA,SAAS,SAAA,CACP,GAAA,EACA,IAAA,EACA,IAAA,EACM;AACN,EAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAS,aAAA,EAAc,GAAI,kBAAkB,IAAI,CAAA;AAC/D,EAAA,MAAM,eAAA,GAAkB,OAAA,IAAW,eAAA,CAAgB,IAAI,CAAA;AACvD,EAAA,MAAM,MAAA,GAAS,aAAA,IAAiB,cAAA,CAAe,IAAI,CAAA;AAEnD,EAAA,MAAM,QAAA,GAAW,GAAA,CAAI,GAAA,CAAI,IAAI,CAAA;AAC7B,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,KAAA,MAAW,CAAA,IAAK,eAAA,EAAiB,QAAA,CAAS,OAAA,CAAQ,IAAI,CAAC,CAAA;AACvD,IAAA,IAAI,MAAA,WAAiB,MAAA,GAAS,IAAA;AAAA,EAChC,CAAA,MAAO;AACL,IAAA,GAAA,CAAI,GAAA,CAAI,IAAA,EAAM,EAAE,IAAA,EAAM,OAAA,EAAS,IAAI,GAAA,CAAI,eAAe,CAAA,EAAG,MAAA,EAAQ,CAAA;AAAA,EACnE;AACF;AAEA,SAAS,kBAAkB,KAAA,EAAgC;AACzD,EAAA,MAAM,aAAA,GAAgB,CAAC,GAAG,KAAA,CAAM,OAAO,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AAC7D,EAAA,MAAM,WAAqB,EAAC;AAE5B,EAAA,IAAI,MAAM,MAAA,EAAQ;AAChB,IAAA,QAAA,CAAS,KAAK,MAAM,CAAA;AAAA,EACtB;AAEA,EAAA,IAAI,aAAA,CAAc,SAAS,CAAA,EAAG;AAC5B,IAAA,MAAM,UAAA,GAAa,MAAM,MAAA,GACrB,aAAA,CACG,QAAQ,CAAC,CAAA,KAAM,CAAC,CAAA,EAAA,EAAK,CAAC,CAAA,CAAA,EAAI,KAAK,CAAC,CAAA,CAAE,CAAC,CAAA,CACnC,IAAA,EAAK,CACL,KAAK,GAAG,CAAA,GACX,aAAA,CAAc,IAAA,CAAK,GAAG,CAAA;AAC1B,IAAA,QAAA,CAAS,IAAA,CAAK,CAAA,KAAA,EAAQ,UAAU,CAAA,CAAE,CAAA;AAAA,EACpC;AAEA,EAAA,IAAI,QAAA,CAAS,SAAS,CAAA,EAAG;AACvB,IAAA,OAAO,GAAG,KAAA,CAAM,IAAI,IAAI,QAAA,CAAS,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA;AAAA,EAC5C;AACA,EAAA,OAAO,KAAA,CAAM,IAAA;AACf;AAEO,SAAS,eAAe,KAAA,EAKpB;AACT,EAAA,MAAM,MAAA,uBAAa,GAAA,EAA6B;AAEhD,EAAA,SAAA,CAAU,MAAA,EAAQ,QAAA,EAAU,KAAA,CAAM,MAAM,CAAA;AACxC,EAAA,SAAA,CAAU,MAAA,EAAQ,MAAA,EAAQ,KAAA,CAAM,IAAI,CAAA;AACpC,EAAA,SAAA,CAAU,MAAA,EAAQ,MAAA,EAAQ,KAAA,CAAM,IAAI,CAAA;AAEpC,EAAA,IAAI,MAAM,KAAA,EAAO;AACf,IAAA,SAAA,CAAU,MAAA,EAAQ,OAAA,EAAS,KAAA,CAAM,KAAK,CAAA;AAAA,EACxC;AAEA,EAAA,MAAM,QAAA,GAAW,CAAC,GAAG,MAAA,CAAO,QAAQ,CAAA,CAAE,IAAI,iBAAiB,CAAA;AAC3D,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,OAAA,EAAU,kBAAA,CAAmB,CAAC,CAAC,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAC9E,EAAA,OAAO,qCAAqC,MAAM,CAAA,aAAA,CAAA;AACpD;;;AC9GA,IAAM,WAAA,GAAsC;AAAA,EAC1C,QAAA,EAAU,KAAA;AAAA,EACV,IAAA,EAAM,MAAA;AAAA,EACN,KAAA,EAAO,OAAA;AAAA,EACP,QAAA,EAAU;AACZ,CAAA;AAEA,IAAM,cAAA,GACJ,2FAAA;AAEK,SAAS,kBAAA,CAAmB,YAAoB,OAAA,EAAsC;AAC3F,EAAA,MAAM,YAA8B,EAAC;AAErC,EAAA,MAAM,sBAAsB,UAAA,CAAW,OAAA;AAAA,IACrC,cAAA;AAAA,IACA,CAAC,KAAA,EAAO,GAAA,EAAa,QAAA,EAAkB,MAAA,KAAmB;AACxD,MAAA,MAAM,SAAA,GAAY,WAAA,CAAY,MAAM,CAAA,IAAK,MAAA;AACzC,MAAA,MAAM,eAAe,CAAA,QAAA,EAAW,OAAO,CAAA,cAAA,EAAiB,QAAQ,IAAI,SAAS,CAAA,CAAA;AAC7E,MAAA,SAAA,CAAU,IAAA,CAAK,EAAE,GAAA,EAAK,QAAA,EAAU,WAAW,CAAA;AAC3C,MAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,GAAA,EAAK,YAAY,CAAA;AAAA,IACxC;AAAA,GACF;AAEA,EAAA,OAAO,EAAE,qBAAqB,SAAA,EAAU;AAC1C;;;ACnBA,IAAM,qBAAA,GAAwB,mBAAA;AAC9B,IAAM,mBAAA,GAAsB,iBAAA;AAC5B,IAAM,mBAAA,GAAsB,eAAA;AAE5B,IAAM,cAAA,GAA+B;AAAA,EACnC,aAAA,EAAe,IAAA;AAAA,EACf,UAAA,EAAY;AACd,CAAA;AAEO,IAAM,YAAA,GAA2D,CACtE,WAAA,KACG;AACH,EAAA,MAAM,OAAA,GAAwB,EAAE,GAAG,cAAA,EAAgB,GAAG,WAAA,EAAY;AAElE,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,cAAA;AAAA,IACN,OAAO,IAAA,CAAK,GAAA,EAAe,QAAA,EAAqB,UAAA,EAA+C;AAC7F,MAAA,IAAI,OAAA,CAAQ,eAAe,YAAA,EAAc;AACvC,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,OAAA,GAAU,GAAA,CAAI,GAAA,CAAI,aAAA,CAAc,OAAA;AACtC,MAAA,IAAI,CAAC,OAAA,EAAS;AACZ,QAAA,MAAM,IAAI,MAAM,0DAA0D,CAAA;AAAA,MAC5E;AAEA,MAAA,MAAM,UAAA,GAAa,QAAQ,MAAA,IAAU,qBAAA;AACrC,MAAA,MAAM,QAAA,GAAW,QAAQ,IAAA,IAAQ,mBAAA;AACjC,MAAA,MAAM,QAAA,GAAW,QAAQ,IAAA,IAAQ,mBAAA;AAEjC,MAAA,MAAM,OAAO,cAAA,CAAe;AAAA,QAC1B,OAAO,OAAA,CAAQ,KAAA;AAAA,QACf,MAAA,EAAQ,UAAA;AAAA,QACR,IAAA,EAAM,QAAA;AAAA,QACN,IAAA,EAAM;AAAA,OACP,CAAA;AAED,MAAA,MAAM,WAAA,GAAc,MAAM,KAAA,CAAM,IAAI,CAAA;AACpC,MAAA,IAAI,CAAC,YAAY,EAAA,EAAI;AACnB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,iDAAA,EAAoD,WAAA,CAAY,MAAM,CAAA,CAAA,EAAI,YAAY,UAAU,CAAA;AAAA,SAClG;AAAA,MACF;AACA,MAAA,MAAM,OAAA,GAAU,MAAM,WAAA,CAAY,IAAA,EAAK;AAEvC,MAAA,MAAM,EAAE,mBAAA,EAAqB,SAAA,EAAU,GAAI,kBAAA,CAAmB,SAAS,OAAO,CAAA;AAE9E,MAAA,MAAM,WAAW,IAAA,CAAK,IAAA,CAAK,IAAI,IAAA,CAAK,MAAA,EAAQ,UAAU,OAAO,CAAA;AAC7D,MAAA,MAAM,GAAG,QAAA,CAAS,KAAA,CAAM,UAAU,EAAE,SAAA,EAAW,MAAM,CAAA;AAErD,MAAA,KAAA,MAAW,YAAY,SAAA,EAAW;AAChC,QAAA,MAAM,YAAA,GAAe,MAAM,KAAA,CAAM,QAAA,CAAS,GAAG,CAAA;AAC7C,QAAA,IAAI,CAAC,aAAa,EAAA,EAAI;AACpB,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,0CAAA,EAA6C,SAAS,GAAG,CAAA,EAAA,EAAK,aAAa,MAAM,CAAA,CAAA,EAAI,aAAa,UAAU,CAAA,CAAA;AAAA,WAC9G;AAAA,QACF;AACA,QAAA,MAAM,GAAA,GAAM,MAAM,YAAA,CAAa,WAAA,EAAY;AAC3C,QAAA,MAAM,QAAA,GAAW,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,CAAA,EAAG,SAAS,QAAQ,CAAA,CAAA,EAAI,QAAA,CAAS,SAAS,CAAA,CAAE,CAAA;AACjF,QAAA,MAAM,GAAG,QAAA,CAAS,SAAA,CAAU,UAAU,MAAA,CAAO,IAAA,CAAK,GAAG,CAAC,CAAA;AACtD,QAAA,MAAM,QAAA;AAAA,MACR;AAEA,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,kBAAkB,CAAA;AACtD,MAAA,MAAM,EAAA,CAAG,QAAA,CAAS,SAAA,CAAU,OAAA,EAAS,mBAAmB,CAAA;AACxD,MAAA,MAAM,OAAA;AAAA,IACR;AAAA,GACF;AACF","file":"emitter.js","sourcesContent":["export const OBSIDIAN_SANS_STACK =\n 'ui-sans-serif, -apple-system, BlinkMacSystemFont, system-ui, \"Segoe UI\", Roboto, \"Inter\", \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", sans-serif';\n\nexport const OBSIDIAN_MONO_STACK =\n 'ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Roboto Mono\", \"DejaVu Sans Mono\", \"Liberation Mono\", Menlo, Monaco, \"Consolas\", \"Source Code Pro\", monospace';\n\nexport type FontRole = \"title\" | \"header\" | \"body\" | \"code\";\n\nexport const DEFAULT_WEIGHTS: Record = {\n title: [400, 700],\n header: [400, 700],\n body: [400, 600],\n code: [400, 600],\n};\n\nexport const DEFAULT_ITALIC: Record = {\n title: false,\n header: false,\n body: true,\n code: false,\n};\n","import type { FontSpecification } from \"../types\";\nimport type { FontRole } from \"../defaults\";\nimport { DEFAULT_WEIGHTS, DEFAULT_ITALIC } from \"../defaults\";\n\nfunction normalizeFontSpec(spec: FontSpecification): {\n name: string;\n weights?: number[];\n includeItalic?: boolean;\n} {\n return typeof spec === \"string\" ? { name: spec } : spec;\n}\n\nexport function getFontName(spec: FontSpecification): string {\n return typeof spec === \"string\" ? spec : spec.name;\n}\n\nexport function formatFontSpecification(role: FontRole, spec: FontSpecification): string {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const features: string[] = [];\n if (italic) {\n features.push(\"ital\");\n }\n\n if (resolvedWeights.length > 1) {\n const weightSpec = italic\n ? resolvedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : resolvedWeights.join(\";\");\n\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${name}:${features.join(\",\")}`;\n }\n\n return name;\n}\n\ninterface MergedFontEntry {\n name: string;\n weights: Set;\n italic: boolean;\n}\n\nfunction mergeInto(\n map: Map,\n role: FontRole,\n spec: FontSpecification,\n): void {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const existing = map.get(name);\n if (existing) {\n for (const w of resolvedWeights) existing.weights.add(w);\n if (italic) existing.italic = true;\n } else {\n map.set(name, { name, weights: new Set(resolvedWeights), italic });\n }\n}\n\nfunction formatMergedEntry(entry: MergedFontEntry): string {\n const sortedWeights = [...entry.weights].sort((a, b) => a - b);\n const features: string[] = [];\n\n if (entry.italic) {\n features.push(\"ital\");\n }\n\n if (sortedWeights.length > 1) {\n const weightSpec = entry.italic\n ? sortedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : sortedWeights.join(\";\");\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${entry.name}:${features.join(\",\")}`;\n }\n return entry.name;\n}\n\nexport function googleFontHref(fonts: {\n title?: FontSpecification;\n header: FontSpecification;\n body: FontSpecification;\n code: FontSpecification;\n}): string {\n const merged = new Map();\n\n mergeInto(merged, \"header\", fonts.header);\n mergeInto(merged, \"body\", fonts.body);\n mergeInto(merged, \"code\", fonts.code);\n\n if (fonts.title) {\n mergeInto(merged, \"title\", fonts.title);\n }\n\n const families = [...merged.values()].map(formatMergedEntry);\n const params = families.map((f) => `family=${encodeURIComponent(f)}`).join(\"&\");\n return `https://fonts.googleapis.com/css2?${params}&display=swap`;\n}\n","import type { ProcessedFontResult, GoogleFontFile } from \"../types\";\n\nconst fontMimeMap: Record = {\n truetype: \"ttf\",\n woff: \"woff\",\n woff2: \"woff2\",\n opentype: \"otf\",\n};\n\nconst fontUrlPattern =\n /url\\((https:\\/\\/fonts.gstatic.com\\/.+(?:\\/|(?:kit=))(.+?)[.&].+?)\\)\\sformat\\('(\\w+?)'\\);/g;\n\nexport function processGoogleFonts(stylesheet: string, baseUrl: string): ProcessedFontResult {\n const fontFiles: GoogleFontFile[] = [];\n\n const processedStylesheet = stylesheet.replace(\n fontUrlPattern,\n (match, url: string, filename: string, format: string) => {\n const extension = fontMimeMap[format] ?? format;\n const rewrittenUrl = `https://${baseUrl}/static/fonts/${filename}.${extension}`;\n fontFiles.push({ url, filename, extension });\n return match.replace(url, rewrittenUrl);\n },\n );\n\n return { processedStylesheet, fontFiles };\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport type { QuartzEmitterPlugin, BuildCtx, FilePath } from \"@quartz-community/types\";\nimport type { FontsOptions } from \"./types\";\nimport { googleFontHref } from \"./util/google-fonts\";\nimport { processGoogleFonts } from \"./util/process-fonts\";\n\nconst QUARTZ_DEFAULT_HEADER = \"Schibsted Grotesk\";\nconst QUARTZ_DEFAULT_BODY = \"Source Sans Pro\";\nconst QUARTZ_DEFAULT_CODE = \"IBM Plex Mono\";\n\nconst defaultOptions: FontsOptions = {\n useThemeFonts: true,\n fontOrigin: \"googleFonts\",\n};\n\nexport const FontsEmitter: QuartzEmitterPlugin> = (\n userOptions?: Partial,\n) => {\n const options: FontsOptions = { ...defaultOptions, ...userOptions };\n\n return {\n name: \"FontsEmitter\",\n async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator {\n if (options.fontOrigin !== \"selfHosted\") {\n return;\n }\n\n const baseUrl = ctx.cfg.configuration.baseUrl;\n if (!baseUrl) {\n throw new Error(\"[FontsEmitter] baseUrl is required for selfHosted fonts.\");\n }\n\n const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;\n const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;\n const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;\n\n const href = googleFontHref({\n title: options.title,\n header: headerSpec,\n body: bodySpec,\n code: codeSpec,\n });\n\n const cssResponse = await fetch(href);\n if (!cssResponse.ok) {\n throw new Error(\n `[FontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`,\n );\n }\n const cssText = await cssResponse.text();\n\n const { processedStylesheet, fontFiles } = processGoogleFonts(cssText, baseUrl);\n\n const fontsDir = path.join(ctx.argv.output, \"static\", \"fonts\");\n await fs.promises.mkdir(fontsDir, { recursive: true });\n\n for (const fontFile of fontFiles) {\n const fontResponse = await fetch(fontFile.url);\n if (!fontResponse.ok) {\n throw new Error(\n `[FontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`,\n );\n }\n const buf = await fontResponse.arrayBuffer();\n const filePath = path.join(fontsDir, `${fontFile.filename}.${fontFile.extension}`);\n await fs.promises.writeFile(filePath, Buffer.from(buf));\n yield filePath as FilePath;\n }\n\n const cssPath = path.join(fontsDir, \"quartz-fonts.css\");\n await fs.promises.writeFile(cssPath, processedStylesheet);\n yield cssPath as FilePath;\n },\n };\n};\n"]} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts index 6d4a5d7..2b2f9d7 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,9 +1,9 @@ import { QuartzTransformerPlugin } from '@quartz-community/types'; export { QuartzEmitterPlugin, QuartzTransformerPlugin } from '@quartz-community/types'; -import { QuartzFontsOptions } from './types.js'; +import { FontsOptions } from './types.js'; export { FontFileEntry, FontSpecification, GoogleFontFile, ProcessedFontResult, QuartzFontRegistry } from './types.js'; -export { QuartzFontsEmitter } from './emitter.js'; +export { FontsEmitter } from './emitter.js'; -declare const QuartzFonts: QuartzTransformerPlugin>; +declare const Fonts: QuartzTransformerPlugin>; -export { QuartzFonts, QuartzFontsOptions }; +export { Fonts, FontsOptions }; diff --git a/dist/index.js b/dist/index.js index ab89f51..27e1d34 100644 --- a/dist/index.js +++ b/dist/index.js @@ -177,7 +177,7 @@ function resolveFonts(options) { if (options.useThemeFonts !== false && !registry) { if (isQuartzThemeEnabled()) { console.warn( - "[QuartzFonts] QuartzTheme is enabled but its font registry is empty. Ensure QuartzTheme runs before QuartzFonts (lower defaultOrder number). Falling back to Obsidian defaults." + "[Fonts] QuartzTheme is enabled but its font registry is empty. Ensure QuartzTheme runs before Fonts (lower defaultOrder number). Falling back to Obsidian defaults." ); } } @@ -251,7 +251,7 @@ function runValidation(options) { for (const { role, spec, fontRole } of specs) { const warnings = validateFontSpec(role, spec, fontRole); for (const w2 of warnings) { - console.warn(`[QuartzFonts] ${w2.role}: ${w2.message}`); + console.warn(`[Fonts] ${w2.role}: ${w2.message}`); } } } @@ -302,13 +302,13 @@ function buildGoogleFontsHead(options) { _("link", { rel: "stylesheet", href }) ]; } -var QuartzFonts = (userOptions) => { +var Fonts = (userOptions) => { const options = { ...defaultOptions, ...userOptions }; if (options.fontOrigin === "googleFonts") { runValidation(options); } return { - name: "QuartzFonts", + name: "Fonts", textTransform(_ctx, src) { return src; }, @@ -359,17 +359,17 @@ var defaultOptions2 = { useThemeFonts: true, fontOrigin: "googleFonts" }; -var QuartzFontsEmitter = (userOptions) => { +var FontsEmitter = (userOptions) => { const options = { ...defaultOptions2, ...userOptions }; return { - name: "QuartzFontsEmitter", + name: "FontsEmitter", async *emit(ctx, _content, _resources) { if (options.fontOrigin !== "selfHosted") { return; } const baseUrl = ctx.cfg.configuration.baseUrl; if (!baseUrl) { - throw new Error("[QuartzFontsEmitter] baseUrl is required for selfHosted fonts."); + throw new Error("[FontsEmitter] baseUrl is required for selfHosted fonts."); } const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER2; const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY2; @@ -383,7 +383,7 @@ var QuartzFontsEmitter = (userOptions) => { const cssResponse = await fetch(href); if (!cssResponse.ok) { throw new Error( - `[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}` + `[FontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}` ); } const cssText = await cssResponse.text(); @@ -394,7 +394,7 @@ var QuartzFontsEmitter = (userOptions) => { const fontResponse = await fetch(fontFile.url); if (!fontResponse.ok) { throw new Error( - `[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})` + `[FontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})` ); } const buf = await fontResponse.arrayBuffer(); @@ -409,6 +409,6 @@ var QuartzFontsEmitter = (userOptions) => { }; }; -export { QuartzFonts, QuartzFontsEmitter }; +export { Fonts, FontsEmitter }; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map index f81b6c2..127995f 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../node_modules/preact/src/constants.js","../node_modules/preact/src/util.js","../node_modules/preact/src/options.js","../node_modules/preact/src/create-element.js","../node_modules/preact/src/diff/catch-error.js","../node_modules/preact/src/component.js","../node_modules/preact/src/create-context.js","../src/defaults.ts","../src/util/registry.ts","../src/util/google-fonts.ts","../src/util/validate.ts","../src/transformer.ts","../src/util/process-fonts.ts","../src/emitter.ts"],"names":["slice","options","vnodeId","EMPTY_ARR","createElement","type","props","children","key","ref","i","normalizedProps","arguments","length","call","defaultProps","createVNode","original","vnode","__k","__","__b","__e","__c","constructor","__v","__i","__u","error","oldVNode","errorInfo","component","ctor","handled","getDerivedStateFromError","setState","__d","componentDidCatch","__E","e","Promise","prototype","then","bind","resolve","setTimeout","w","a","f","QUARTZ_DEFAULT_HEADER","QUARTZ_DEFAULT_BODY","QUARTZ_DEFAULT_CODE","defaultOptions"],"mappings":"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","file":"index.js","sourcesContent":["/** Normal hydration that attaches to a DOM tree but does not diff it. */\nexport const MODE_HYDRATE = 1 << 5;\n/** Signifies this VNode suspended on the previous render */\nexport const MODE_SUSPENDED = 1 << 7;\n/** Indicates that this node needs to be inserted while patching children */\nexport const INSERT_VNODE = 1 << 2;\n/** Indicates a VNode has been matched with another VNode in the diff */\nexport const MATCHED = 1 << 1;\n\n/** Reset all mode flags */\nexport const RESET_MODE = ~(MODE_HYDRATE | MODE_SUSPENDED);\n\nexport const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\nexport const XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\nexport const MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n\nexport const NULL = null;\nexport const UNDEFINED = undefined;\nexport const EMPTY_OBJ = /** @type {any} */ ({});\nexport const EMPTY_ARR = [];\nexport const IS_NON_DIMENSIONAL =\n\t/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;\n","import { EMPTY_ARR } from './constants';\n\nexport const isArray = Array.isArray;\n\n/**\n * Assign properties from `props` to `obj`\n * @template O, P The obj and props types\n * @param {O} obj The object to copy properties to\n * @param {P} props The object to copy properties from\n * @returns {O & P}\n */\nexport function assign(obj, props) {\n\t// @ts-expect-error We change the type of `obj` to be `O & P`\n\tfor (let i in props) obj[i] = props[i];\n\treturn /** @type {O & P} */ (obj);\n}\n\n/**\n * Remove a child node from its parent if attached. This is a workaround for\n * IE11 which doesn't support `Element.prototype.remove()`. Using this function\n * is smaller than including a dedicated polyfill.\n * @param {import('./index').ContainerNode} node The node to remove\n */\nexport function removeNode(node) {\n\tif (node && node.parentNode) node.parentNode.removeChild(node);\n}\n\nexport const slice = EMPTY_ARR.slice;\n","import { _catchError } from './diff/catch-error';\n\n/**\n * The `option` object can potentially contain callback functions\n * that are called during various stages of our renderer. This is the\n * foundation on which all our addons like `preact/debug`, `preact/compat`,\n * and `preact/hooks` are based on. See the `Options` type in `internal.d.ts`\n * for a full list of available option hooks (most editors/IDEs allow you to\n * ctrl+click or cmd+click on mac the type definition below).\n * @type {import('./internal').Options}\n */\nconst options = {\n\t_catchError\n};\n\nexport default options;\n","import { slice } from './util';\nimport options from './options';\nimport { NULL, UNDEFINED } from './constants';\n\nlet vnodeId = 0;\n\n/**\n * Create an virtual node (used for JSX)\n * @param {import('./internal').VNode[\"type\"]} type The node name or Component constructor for this\n * virtual node\n * @param {object | null | undefined} [props] The properties of the virtual node\n * @param {Array} [children] The children of the\n * virtual node\n * @returns {import('./internal').VNode}\n */\nexport function createElement(type, props, children) {\n\tlet normalizedProps = {},\n\t\tkey,\n\t\tref,\n\t\ti;\n\tfor (i in props) {\n\t\tif (i == 'key') key = props[i];\n\t\telse if (i == 'ref') ref = props[i];\n\t\telse normalizedProps[i] = props[i];\n\t}\n\n\tif (arguments.length > 2) {\n\t\tnormalizedProps.children =\n\t\t\targuments.length > 3 ? slice.call(arguments, 2) : children;\n\t}\n\n\t// If a Component VNode, check for and apply defaultProps\n\t// Note: type may be undefined in development, must never error here.\n\tif (typeof type == 'function' && type.defaultProps != NULL) {\n\t\tfor (i in type.defaultProps) {\n\t\t\tif (normalizedProps[i] === UNDEFINED) {\n\t\t\t\tnormalizedProps[i] = type.defaultProps[i];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn createVNode(type, normalizedProps, key, ref, NULL);\n}\n\n/**\n * Create a VNode (used internally by Preact)\n * @param {import('./internal').VNode[\"type\"]} type The node name or Component\n * Constructor for this virtual node\n * @param {object | string | number | null} props The properties of this virtual node.\n * If this virtual node represents a text node, this is the text of the node (string or number).\n * @param {string | number | null} key The key for this virtual node, used when\n * diffing it against its children\n * @param {import('./internal').VNode[\"ref\"]} ref The ref property that will\n * receive a reference to its created child\n * @returns {import('./internal').VNode}\n */\nexport function createVNode(type, props, key, ref, original) {\n\t// V8 seems to be better at detecting type shapes if the object is allocated from the same call site\n\t// Do not inline into createElement and coerceToVNode!\n\t/** @type {import('./internal').VNode} */\n\tconst vnode = {\n\t\ttype,\n\t\tprops,\n\t\tkey,\n\t\tref,\n\t\t_children: NULL,\n\t\t_parent: NULL,\n\t\t_depth: 0,\n\t\t_dom: NULL,\n\t\t_component: NULL,\n\t\tconstructor: UNDEFINED,\n\t\t_original: original == NULL ? ++vnodeId : original,\n\t\t_index: -1,\n\t\t_flags: 0\n\t};\n\n\t// Only invoke the vnode hook if this was *not* a direct copy:\n\tif (original == NULL && options.vnode != NULL) options.vnode(vnode);\n\n\treturn vnode;\n}\n\nexport function createRef() {\n\treturn { current: NULL };\n}\n\nexport function Fragment(props) {\n\treturn props.children;\n}\n\n/**\n * Check if a the argument is a valid Preact VNode.\n * @param {*} vnode\n * @returns {vnode is VNode}\n */\nexport const isValidElement = vnode =>\n\tvnode != NULL && vnode.constructor === UNDEFINED;\n","import { NULL } from '../constants';\n\n/**\n * Find the closest error boundary to a thrown error and call it\n * @param {object} error The thrown value\n * @param {import('../internal').VNode} vnode The vnode that threw the error that was caught (except\n * for unmounting when this parameter is the highest parent that was being\n * unmounted)\n * @param {import('../internal').VNode} [oldVNode]\n * @param {import('../internal').ErrorInfo} [errorInfo]\n */\nexport function _catchError(error, vnode, oldVNode, errorInfo) {\n\t/** @type {import('../internal').Component} */\n\tlet component,\n\t\t/** @type {import('../internal').ComponentType} */\n\t\tctor,\n\t\t/** @type {boolean} */\n\t\thandled;\n\n\tfor (; (vnode = vnode._parent); ) {\n\t\tif ((component = vnode._component) && !component._processingException) {\n\t\t\ttry {\n\t\t\t\tctor = component.constructor;\n\n\t\t\t\tif (ctor && ctor.getDerivedStateFromError != NULL) {\n\t\t\t\t\tcomponent.setState(ctor.getDerivedStateFromError(error));\n\t\t\t\t\thandled = component._dirty;\n\t\t\t\t}\n\n\t\t\t\tif (component.componentDidCatch != NULL) {\n\t\t\t\t\tcomponent.componentDidCatch(error, errorInfo || {});\n\t\t\t\t\thandled = component._dirty;\n\t\t\t\t}\n\n\t\t\t\t// This is an error boundary. Mark it as having bailed out, and whether it was mid-hydration.\n\t\t\t\tif (handled) {\n\t\t\t\t\treturn (component._pendingError = component);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\terror = e;\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow error;\n}\n","import { assign } from './util';\nimport { diff, commitRoot } from './diff/index';\nimport options from './options';\nimport { Fragment } from './create-element';\nimport { MODE_HYDRATE, NULL } from './constants';\n\n/**\n * Base Component class. Provides `setState()` and `forceUpdate()`, which\n * trigger rendering\n * @param {object} props The initial component props\n * @param {object} context The initial context from parent components'\n * getChildContext\n */\nexport function BaseComponent(props, context) {\n\tthis.props = props;\n\tthis.context = context;\n}\n\n/**\n * Update component state and schedule a re-render.\n * @this {import('./internal').Component}\n * @param {object | ((s: object, p: object) => object)} update A hash of state\n * properties to update with new values or a function that given the current\n * state and props returns a new partial state\n * @param {() => void} [callback] A function to be called once component state is\n * updated\n */\nBaseComponent.prototype.setState = function (update, callback) {\n\t// only clone state when copying to nextState the first time.\n\tlet s;\n\tif (this._nextState != NULL && this._nextState != this.state) {\n\t\ts = this._nextState;\n\t} else {\n\t\ts = this._nextState = assign({}, this.state);\n\t}\n\n\tif (typeof update == 'function') {\n\t\t// Some libraries like `immer` mark the current state as readonly,\n\t\t// preventing us from mutating it, so we need to clone it. See #2716\n\t\tupdate = update(assign({}, s), this.props);\n\t}\n\n\tif (update) {\n\t\tassign(s, update);\n\t}\n\n\t// Skip update if updater function returned null\n\tif (update == NULL) return;\n\n\tif (this._vnode) {\n\t\tif (callback) {\n\t\t\tthis._stateCallbacks.push(callback);\n\t\t}\n\t\tenqueueRender(this);\n\t}\n};\n\n/**\n * Immediately perform a synchronous re-render of the component\n * @this {import('./internal').Component}\n * @param {() => void} [callback] A function to be called after component is\n * re-rendered\n */\nBaseComponent.prototype.forceUpdate = function (callback) {\n\tif (this._vnode) {\n\t\t// Set render mode so that we can differentiate where the render request\n\t\t// is coming from. We need this because forceUpdate should never call\n\t\t// shouldComponentUpdate\n\t\tthis._force = true;\n\t\tif (callback) this._renderCallbacks.push(callback);\n\t\tenqueueRender(this);\n\t}\n};\n\n/**\n * Accepts `props` and `state`, and returns a new Virtual DOM tree to build.\n * Virtual DOM is generally constructed via [JSX](https://jasonformat.com/wtf-is-jsx).\n * @param {object} props Props (eg: JSX attributes) received from parent\n * element/component\n * @param {object} state The component's current state\n * @param {object} context Context object, as returned by the nearest\n * ancestor's `getChildContext()`\n * @returns {ComponentChildren | void}\n */\nBaseComponent.prototype.render = Fragment;\n\n/**\n * @param {import('./internal').VNode} vnode\n * @param {number | null} [childIndex]\n */\nexport function getDomSibling(vnode, childIndex) {\n\tif (childIndex == NULL) {\n\t\t// Use childIndex==null as a signal to resume the search from the vnode's sibling\n\t\treturn vnode._parent\n\t\t\t? getDomSibling(vnode._parent, vnode._index + 1)\n\t\t\t: NULL;\n\t}\n\n\tlet sibling;\n\tfor (; childIndex < vnode._children.length; childIndex++) {\n\t\tsibling = vnode._children[childIndex];\n\n\t\tif (sibling != NULL && sibling._dom != NULL) {\n\t\t\t// Since updateParentDomPointers keeps _dom pointer correct,\n\t\t\t// we can rely on _dom to tell us if this subtree contains a\n\t\t\t// rendered DOM node, and what the first rendered DOM node is\n\t\t\treturn sibling._dom;\n\t\t}\n\t}\n\n\t// If we get here, we have not found a DOM node in this vnode's children.\n\t// We must resume from this vnode's sibling (in it's parent _children array)\n\t// Only climb up and search the parent if we aren't searching through a DOM\n\t// VNode (meaning we reached the DOM parent of the original vnode that began\n\t// the search)\n\treturn typeof vnode.type == 'function' ? getDomSibling(vnode) : NULL;\n}\n\n/**\n * Trigger in-place re-rendering of a component.\n * @param {import('./internal').Component} component The component to rerender\n */\nfunction renderComponent(component) {\n\tlet oldVNode = component._vnode,\n\t\toldDom = oldVNode._dom,\n\t\tcommitQueue = [],\n\t\trefQueue = [];\n\n\tif (component._parentDom) {\n\t\tconst newVNode = assign({}, oldVNode);\n\t\tnewVNode._original = oldVNode._original + 1;\n\t\tif (options.vnode) options.vnode(newVNode);\n\n\t\tdiff(\n\t\t\tcomponent._parentDom,\n\t\t\tnewVNode,\n\t\t\toldVNode,\n\t\t\tcomponent._globalContext,\n\t\t\tcomponent._parentDom.namespaceURI,\n\t\t\toldVNode._flags & MODE_HYDRATE ? [oldDom] : NULL,\n\t\t\tcommitQueue,\n\t\t\toldDom == NULL ? getDomSibling(oldVNode) : oldDom,\n\t\t\t!!(oldVNode._flags & MODE_HYDRATE),\n\t\t\trefQueue\n\t\t);\n\n\t\tnewVNode._original = oldVNode._original;\n\t\tnewVNode._parent._children[newVNode._index] = newVNode;\n\t\tcommitRoot(commitQueue, newVNode, refQueue);\n\t\toldVNode._dom = oldVNode._parent = null;\n\n\t\tif (newVNode._dom != oldDom) {\n\t\t\tupdateParentDomPointers(newVNode);\n\t\t}\n\t}\n}\n\n/**\n * @param {import('./internal').VNode} vnode\n */\nfunction updateParentDomPointers(vnode) {\n\tif ((vnode = vnode._parent) != NULL && vnode._component != NULL) {\n\t\tvnode._dom = vnode._component.base = NULL;\n\t\tfor (let i = 0; i < vnode._children.length; i++) {\n\t\t\tlet child = vnode._children[i];\n\t\t\tif (child != NULL && child._dom != NULL) {\n\t\t\t\tvnode._dom = vnode._component.base = child._dom;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn updateParentDomPointers(vnode);\n\t}\n}\n\n/**\n * The render queue\n * @type {Array}\n */\nlet rerenderQueue = [];\n\n/*\n * The value of `Component.debounce` must asynchronously invoke the passed in callback. It is\n * important that contributors to Preact can consistently reason about what calls to `setState`, etc.\n * do, and when their effects will be applied. See the links below for some further reading on designing\n * asynchronous APIs.\n * * [Designing APIs for Asynchrony](https://blog.izs.me/2013/08/designing-apis-for-asynchrony)\n * * [Callbacks synchronous and asynchronous](https://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/)\n */\n\nlet prevDebounce;\n\nconst defer =\n\ttypeof Promise == 'function'\n\t\t? Promise.prototype.then.bind(Promise.resolve())\n\t\t: setTimeout;\n\n/**\n * Enqueue a rerender of a component\n * @param {import('./internal').Component} c The component to rerender\n */\nexport function enqueueRender(c) {\n\tif (\n\t\t(!c._dirty &&\n\t\t\t(c._dirty = true) &&\n\t\t\trerenderQueue.push(c) &&\n\t\t\t!process._rerenderCount++) ||\n\t\tprevDebounce != options.debounceRendering\n\t) {\n\t\tprevDebounce = options.debounceRendering;\n\t\t(prevDebounce || defer)(process);\n\t}\n}\n\n/**\n * @param {import('./internal').Component} a\n * @param {import('./internal').Component} b\n */\nconst depthSort = (a, b) => a._vnode._depth - b._vnode._depth;\n\n/** Flush the render queue by rerendering all queued components */\nfunction process() {\n\tlet c,\n\t\tl = 1;\n\n\t// Don't update `renderCount` yet. Keep its value non-zero to prevent unnecessary\n\t// process() calls from getting scheduled while `queue` is still being consumed.\n\twhile (rerenderQueue.length) {\n\t\t// Keep the rerender queue sorted by (depth, insertion order). The queue\n\t\t// will initially be sorted on the first iteration only if it has more than 1 item.\n\t\t//\n\t\t// New items can be added to the queue e.g. when rerendering a provider, so we want to\n\t\t// keep the order from top to bottom with those new items so we can handle them in a\n\t\t// single pass\n\t\tif (rerenderQueue.length > l) {\n\t\t\trerenderQueue.sort(depthSort);\n\t\t}\n\n\t\tc = rerenderQueue.shift();\n\t\tl = rerenderQueue.length;\n\n\t\tif (c._dirty) {\n\t\t\trenderComponent(c);\n\t\t}\n\t}\n\tprocess._rerenderCount = 0;\n}\n\nprocess._rerenderCount = 0;\n","import { enqueueRender } from './component';\nimport { NULL } from './constants';\n\nexport let i = 0;\n\nexport function createContext(defaultValue) {\n\tfunction Context(props) {\n\t\tif (!this.getChildContext) {\n\t\t\t/** @type {Set | null} */\n\t\t\tlet subs = new Set();\n\t\t\tlet ctx = {};\n\t\t\tctx[Context._id] = this;\n\n\t\t\tthis.getChildContext = () => ctx;\n\n\t\t\tthis.componentWillUnmount = () => {\n\t\t\t\tsubs = NULL;\n\t\t\t};\n\n\t\t\tthis.shouldComponentUpdate = function (_props) {\n\t\t\t\t// @ts-expect-error even\n\t\t\t\tif (this.props.value != _props.value) {\n\t\t\t\t\tsubs.forEach(c => {\n\t\t\t\t\t\tc._force = true;\n\t\t\t\t\t\tenqueueRender(c);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tthis.sub = c => {\n\t\t\t\tsubs.add(c);\n\t\t\t\tlet old = c.componentWillUnmount;\n\t\t\t\tc.componentWillUnmount = () => {\n\t\t\t\t\tif (subs) {\n\t\t\t\t\t\tsubs.delete(c);\n\t\t\t\t\t}\n\t\t\t\t\tif (old) old.call(c);\n\t\t\t\t};\n\t\t\t};\n\t\t}\n\n\t\treturn props.children;\n\t}\n\n\tContext._id = '__cC' + i++;\n\tContext._defaultValue = defaultValue;\n\n\t/** @type {import('./internal').FunctionComponent} */\n\tContext.Consumer = (props, contextValue) => {\n\t\treturn props.children(contextValue);\n\t};\n\n\t// we could also get rid of _contextRef entirely\n\tContext.Provider =\n\t\tContext._contextRef =\n\t\tContext.Consumer.contextType =\n\t\t\tContext;\n\n\treturn Context;\n}\n","export const OBSIDIAN_SANS_STACK =\n 'ui-sans-serif, -apple-system, BlinkMacSystemFont, system-ui, \"Segoe UI\", Roboto, \"Inter\", \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", sans-serif';\n\nexport const OBSIDIAN_MONO_STACK =\n 'ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Roboto Mono\", \"DejaVu Sans Mono\", \"Liberation Mono\", Menlo, Monaco, \"Consolas\", \"Source Code Pro\", monospace';\n\nexport type FontRole = \"title\" | \"header\" | \"body\" | \"code\";\n\nexport const DEFAULT_WEIGHTS: Record = {\n title: [400, 700],\n header: [400, 700],\n body: [400, 600],\n code: [400, 600],\n};\n\nexport const DEFAULT_ITALIC: Record = {\n title: false,\n header: false,\n body: true,\n code: false,\n};\n","import type { QuartzFontRegistry } from \"../types\";\n\nconst REGISTRY_KEY = \"__quartzFonts\";\n\nexport function readFontRegistry(): QuartzFontRegistry | undefined {\n const registry = (globalThis as Record)[REGISTRY_KEY];\n if (registry && typeof registry === \"object\" && \"themeName\" in registry && \"fonts\" in registry) {\n return registry as QuartzFontRegistry;\n }\n return undefined;\n}\n\nexport function isQuartzThemeEnabled(): boolean {\n return REGISTRY_KEY in (globalThis as Record);\n}\n","import type { FontSpecification } from \"../types\";\nimport type { FontRole } from \"../defaults\";\nimport { DEFAULT_WEIGHTS, DEFAULT_ITALIC } from \"../defaults\";\n\nfunction normalizeFontSpec(spec: FontSpecification): {\n name: string;\n weights?: number[];\n includeItalic?: boolean;\n} {\n return typeof spec === \"string\" ? { name: spec } : spec;\n}\n\nexport function getFontName(spec: FontSpecification): string {\n return typeof spec === \"string\" ? spec : spec.name;\n}\n\nexport function formatFontSpecification(role: FontRole, spec: FontSpecification): string {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const features: string[] = [];\n if (italic) {\n features.push(\"ital\");\n }\n\n if (resolvedWeights.length > 1) {\n const weightSpec = italic\n ? resolvedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : resolvedWeights.join(\";\");\n\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${name}:${features.join(\",\")}`;\n }\n\n return name;\n}\n\ninterface MergedFontEntry {\n name: string;\n weights: Set;\n italic: boolean;\n}\n\nfunction mergeInto(\n map: Map,\n role: FontRole,\n spec: FontSpecification,\n): void {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const existing = map.get(name);\n if (existing) {\n for (const w of resolvedWeights) existing.weights.add(w);\n if (italic) existing.italic = true;\n } else {\n map.set(name, { name, weights: new Set(resolvedWeights), italic });\n }\n}\n\nfunction formatMergedEntry(entry: MergedFontEntry): string {\n const sortedWeights = [...entry.weights].sort((a, b) => a - b);\n const features: string[] = [];\n\n if (entry.italic) {\n features.push(\"ital\");\n }\n\n if (sortedWeights.length > 1) {\n const weightSpec = entry.italic\n ? sortedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : sortedWeights.join(\";\");\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${entry.name}:${features.join(\",\")}`;\n }\n return entry.name;\n}\n\nexport function googleFontHref(fonts: {\n title?: FontSpecification;\n header: FontSpecification;\n body: FontSpecification;\n code: FontSpecification;\n}): string {\n const merged = new Map();\n\n mergeInto(merged, \"header\", fonts.header);\n mergeInto(merged, \"body\", fonts.body);\n mergeInto(merged, \"code\", fonts.code);\n\n if (fonts.title) {\n mergeInto(merged, \"title\", fonts.title);\n }\n\n const families = [...merged.values()].map(formatMergedEntry);\n const params = families.map((f) => `family=${encodeURIComponent(f)}`).join(\"&\");\n return `https://fonts.googleapis.com/css2?${params}&display=swap`;\n}\n","import type { FontSpecification } from \"../types\";\nimport type { FontRole } from \"../defaults\";\nimport { DEFAULT_WEIGHTS, DEFAULT_ITALIC } from \"../defaults\";\n\ninterface GoogleFontEntry {\n family: string;\n id: string;\n weights: number[];\n styles: string[];\n subsets: string[];\n}\n\ntype FontMetadata = Record;\n\nlet metadataCache: FontMetadata | null | false = null;\n\nfunction loadMetadata(): FontMetadata | undefined {\n if (metadataCache === false) return undefined;\n if (metadataCache) return metadataCache;\n\n try {\n // `require` is injected by tsup's banner via createRequire(import.meta.url).\n // Using it directly avoids a duplicate createRequire import in the bundle.\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { APIv1 } = require(\"google-font-metadata\") as { APIv1: FontMetadata };\n metadataCache = APIv1;\n return metadataCache;\n } catch {\n metadataCache = false;\n return undefined;\n }\n}\n\nfunction fontToId(family: string): string {\n return family.toLowerCase().replace(/\\s+/g, \"-\");\n}\n\nexport interface ValidationWarning {\n font: string;\n role: string;\n message: string;\n}\n\nexport function validateFontSpec(\n role: string,\n spec: FontSpecification,\n fontRole: FontRole,\n): ValidationWarning[] {\n const metadata = loadMetadata();\n if (!metadata) return [];\n\n const warnings: ValidationWarning[] = [];\n const name = typeof spec === \"string\" ? spec : spec.name;\n const id = fontToId(name);\n const entry = metadata[id];\n\n if (!entry) {\n warnings.push({\n font: name,\n role,\n message: `\"${name}\" is not a recognized Google Font. It will be requested but may fail to load.`,\n });\n return warnings;\n }\n\n const weights =\n typeof spec === \"object\" && spec.weights ? spec.weights : DEFAULT_WEIGHTS[fontRole];\n const italic =\n typeof spec === \"object\" && spec.includeItalic !== undefined\n ? spec.includeItalic\n : DEFAULT_ITALIC[fontRole];\n\n for (const weight of weights) {\n if (!entry.weights.includes(weight)) {\n warnings.push({\n font: name,\n role,\n message: `\"${name}\" does not support weight ${weight}. Available weights: ${entry.weights.join(\", \")}.`,\n });\n }\n }\n\n if (italic && !entry.styles.includes(\"italic\")) {\n warnings.push({\n font: name,\n role,\n message: `\"${name}\" does not support italic style. Available styles: ${entry.styles.join(\", \")}.`,\n });\n }\n\n return warnings;\n}\n","import { h } from \"preact\";\nimport type { QuartzTransformerPlugin, CSSResource } from \"@quartz-community/types\";\nimport type { FontSpecification, QuartzFontsOptions } from \"./types\";\nimport { OBSIDIAN_SANS_STACK, OBSIDIAN_MONO_STACK } from \"./defaults\";\nimport { readFontRegistry, isQuartzThemeEnabled } from \"./util/registry\";\nimport { googleFontHref } from \"./util/google-fonts\";\nimport { validateFontSpec } from \"./util/validate\";\n\nconst QUARTZ_DEFAULT_HEADER = \"Schibsted Grotesk\";\nconst QUARTZ_DEFAULT_BODY = \"Source Sans Pro\";\nconst QUARTZ_DEFAULT_CODE = \"IBM Plex Mono\";\n\nconst defaultOptions: QuartzFontsOptions = {\n useThemeFonts: true,\n fontOrigin: \"googleFonts\",\n};\n\nfunction toFontFamily(spec: FontSpecification): string {\n if (typeof spec === \"string\") return spec;\n return spec.name;\n}\n\ninterface ResolvedFonts {\n title: string;\n body: string;\n header: string;\n code: string;\n interface: string;\n h1: string;\n h2: string;\n h3: string;\n h4: string;\n h5: string;\n h6: string;\n}\n\nfunction resolveFonts(options: QuartzFontsOptions): ResolvedFonts {\n const registry = options.useThemeFonts !== false ? readFontRegistry() : undefined;\n\n if (options.useThemeFonts !== false && !registry) {\n if (isQuartzThemeEnabled()) {\n console.warn(\n \"[QuartzFonts] QuartzTheme is enabled but its font registry is empty. \" +\n \"Ensure QuartzTheme runs before QuartzFonts (lower defaultOrder number). \" +\n \"Falling back to Obsidian defaults.\",\n );\n }\n }\n\n const themeFonts = registry?.fonts ?? {};\n\n const resolve = (\n spec: FontSpecification | undefined,\n ...fallbacks: (string | undefined)[]\n ): string => {\n if (spec !== undefined) return toFontFamily(spec);\n for (const fb of fallbacks) {\n if (fb !== undefined) return fb;\n }\n return OBSIDIAN_SANS_STACK;\n };\n\n const body = resolve(\n options.body,\n themeFonts[\"--font-text\"],\n QUARTZ_DEFAULT_BODY,\n OBSIDIAN_SANS_STACK,\n );\n const header = resolve(\n options.header,\n themeFonts[\"--font-text\"],\n QUARTZ_DEFAULT_HEADER,\n OBSIDIAN_SANS_STACK,\n );\n const code = resolve(\n options.code,\n themeFonts[\"--font-monospace\"],\n QUARTZ_DEFAULT_CODE,\n OBSIDIAN_MONO_STACK,\n );\n const interfaceFont = resolve(\n options.interface,\n themeFonts[\"--font-interface\"],\n OBSIDIAN_SANS_STACK,\n );\n const title = resolve(options.title, undefined, header);\n\n const resolveHeading = (level: 1 | 2 | 3 | 4 | 5 | 6): string => {\n const levelKey = `h${level}` as keyof QuartzFontsOptions;\n const themeVarKey = `--h${level}-font`;\n const userSpec = options[levelKey] as FontSpecification | undefined;\n\n if (userSpec !== undefined) return toFontFamily(userSpec);\n if (themeFonts[themeVarKey]) return themeFonts[themeVarKey];\n if (options.header !== undefined) return toFontFamily(options.header);\n if (themeFonts[\"--font-text\"]) return themeFonts[\"--font-text\"];\n return header;\n };\n\n return {\n title,\n body,\n header,\n code,\n interface: interfaceFont,\n h1: resolveHeading(1),\n h2: resolveHeading(2),\n h3: resolveHeading(3),\n h4: resolveHeading(4),\n h5: resolveHeading(5),\n h6: resolveHeading(6),\n };\n}\n\nfunction runValidation(options: QuartzFontsOptions): void {\n const specs: Array<{\n role: string;\n spec: FontSpecification;\n fontRole: \"title\" | \"header\" | \"body\" | \"code\";\n }> = [];\n\n if (options.title) specs.push({ role: \"title\", spec: options.title, fontRole: \"title\" });\n if (options.header) specs.push({ role: \"header\", spec: options.header, fontRole: \"header\" });\n if (options.body) specs.push({ role: \"body\", spec: options.body, fontRole: \"body\" });\n if (options.code) specs.push({ role: \"code\", spec: options.code, fontRole: \"code\" });\n\n const headingLevels = [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"] as const;\n for (const level of headingLevels) {\n const spec = options[level];\n if (spec) specs.push({ role: level, spec, fontRole: \"header\" });\n }\n\n for (const { role, spec, fontRole } of specs) {\n const warnings = validateFontSpec(role, spec, fontRole);\n for (const w of warnings) {\n console.warn(`[QuartzFonts] ${w.role}: ${w.message}`);\n }\n }\n}\n\nfunction buildLayeredCSS(fonts: ResolvedFonts): string {\n return [\n \"@layer quartz-fonts {\",\n \" :root {\",\n ` --titleFont: ${fonts.title};`,\n ` --bodyFont: ${fonts.body};`,\n ` --headerFont: ${fonts.header};`,\n ` --codeFont: ${fonts.code};`,\n ` --font-text: ${fonts.body};`,\n ` --font-interface: ${fonts.interface};`,\n ` --font-monospace: ${fonts.code};`,\n ` --h1-font: ${fonts.h1};`,\n ` --h2-font: ${fonts.h2};`,\n ` --h3-font: ${fonts.h3};`,\n ` --h4-font: ${fonts.h4};`,\n ` --h5-font: ${fonts.h5};`,\n ` --h6-font: ${fonts.h6};`,\n \" }\",\n \"}\",\n ].join(\"\\n\");\n}\n\nfunction buildUnlayeredCSS(fonts: ResolvedFonts): string {\n return [\n `h1 { font-family: ${fonts.h1}; }`,\n `h2 { font-family: ${fonts.h2}; }`,\n `h3 { font-family: ${fonts.h3}; }`,\n `h4 { font-family: ${fonts.h4}; }`,\n `h5 { font-family: ${fonts.h5}; }`,\n `h6 { font-family: ${fonts.h6}; }`,\n ].join(\"\\n\");\n}\n\nfunction buildGoogleFontsHead(options: QuartzFontsOptions): unknown[] {\n const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;\n const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;\n const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;\n\n const href = googleFontHref({\n title: options.title,\n header: headerSpec,\n body: bodySpec,\n code: codeSpec,\n });\n\n return [\n h(\"link\", { rel: \"preconnect\", href: \"https://fonts.googleapis.com\" }),\n h(\"link\", { rel: \"preconnect\", href: \"https://fonts.gstatic.com\", crossOrigin: \"anonymous\" }),\n h(\"link\", { rel: \"stylesheet\", href }),\n ];\n}\n\nexport const QuartzFonts: QuartzTransformerPlugin> = (\n userOptions?: Partial,\n) => {\n const options: QuartzFontsOptions = { ...defaultOptions, ...userOptions };\n\n if (options.fontOrigin === \"googleFonts\") {\n runValidation(options);\n }\n\n return {\n name: \"QuartzFonts\",\n textTransform(_ctx, src) {\n return src;\n },\n externalResources(_ctx) {\n const fonts = resolveFonts(options);\n\n const css: CSSResource[] = [\n { content: buildLayeredCSS(fonts), inline: true },\n { content: buildUnlayeredCSS(fonts), inline: true },\n ];\n\n let additionalHead: unknown[] = [];\n if (options.fontOrigin === \"googleFonts\") {\n additionalHead = buildGoogleFontsHead(options);\n } else if (options.fontOrigin === \"selfHosted\") {\n additionalHead = [h(\"link\", { rel: \"stylesheet\", href: \"/static/fonts/quartz-fonts.css\" })];\n }\n\n return { css, js: [], additionalHead };\n },\n };\n};\n","import type { ProcessedFontResult, GoogleFontFile } from \"../types\";\n\nconst fontMimeMap: Record = {\n truetype: \"ttf\",\n woff: \"woff\",\n woff2: \"woff2\",\n opentype: \"otf\",\n};\n\nconst fontUrlPattern =\n /url\\((https:\\/\\/fonts.gstatic.com\\/.+(?:\\/|(?:kit=))(.+?)[.&].+?)\\)\\sformat\\('(\\w+?)'\\);/g;\n\nexport function processGoogleFonts(stylesheet: string, baseUrl: string): ProcessedFontResult {\n const fontFiles: GoogleFontFile[] = [];\n\n const processedStylesheet = stylesheet.replace(\n fontUrlPattern,\n (match, url: string, filename: string, format: string) => {\n const extension = fontMimeMap[format] ?? format;\n const rewrittenUrl = `https://${baseUrl}/static/fonts/${filename}.${extension}`;\n fontFiles.push({ url, filename, extension });\n return match.replace(url, rewrittenUrl);\n },\n );\n\n return { processedStylesheet, fontFiles };\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport type { QuartzEmitterPlugin, BuildCtx, FilePath } from \"@quartz-community/types\";\nimport type { QuartzFontsOptions } from \"./types\";\nimport { googleFontHref } from \"./util/google-fonts\";\nimport { processGoogleFonts } from \"./util/process-fonts\";\n\nconst QUARTZ_DEFAULT_HEADER = \"Schibsted Grotesk\";\nconst QUARTZ_DEFAULT_BODY = \"Source Sans Pro\";\nconst QUARTZ_DEFAULT_CODE = \"IBM Plex Mono\";\n\nconst defaultOptions: QuartzFontsOptions = {\n useThemeFonts: true,\n fontOrigin: \"googleFonts\",\n};\n\nexport const QuartzFontsEmitter: QuartzEmitterPlugin> = (\n userOptions?: Partial,\n) => {\n const options: QuartzFontsOptions = { ...defaultOptions, ...userOptions };\n\n return {\n name: \"QuartzFontsEmitter\",\n async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator {\n if (options.fontOrigin !== \"selfHosted\") {\n return;\n }\n\n const baseUrl = ctx.cfg.configuration.baseUrl;\n if (!baseUrl) {\n throw new Error(\"[QuartzFontsEmitter] baseUrl is required for selfHosted fonts.\");\n }\n\n const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;\n const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;\n const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;\n\n const href = googleFontHref({\n title: options.title,\n header: headerSpec,\n body: bodySpec,\n code: codeSpec,\n });\n\n const cssResponse = await fetch(href);\n if (!cssResponse.ok) {\n throw new Error(\n `[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`,\n );\n }\n const cssText = await cssResponse.text();\n\n const { processedStylesheet, fontFiles } = processGoogleFonts(cssText, baseUrl);\n\n const fontsDir = path.join(ctx.argv.output, \"static\", \"fonts\");\n await fs.promises.mkdir(fontsDir, { recursive: true });\n\n for (const fontFile of fontFiles) {\n const fontResponse = await fetch(fontFile.url);\n if (!fontResponse.ok) {\n throw new Error(\n `[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`,\n );\n }\n const buf = await fontResponse.arrayBuffer();\n const filePath = path.join(fontsDir, `${fontFile.filename}.${fontFile.extension}`);\n await fs.promises.writeFile(filePath, Buffer.from(buf));\n yield filePath as FilePath;\n }\n\n const cssPath = path.join(fontsDir, \"quartz-fonts.css\");\n await fs.promises.writeFile(cssPath, processedStylesheet);\n yield cssPath as FilePath;\n },\n };\n};\n"]} \ No newline at end of file +{"version":3,"sources":["../node_modules/preact/src/constants.js","../node_modules/preact/src/util.js","../node_modules/preact/src/options.js","../node_modules/preact/src/create-element.js","../node_modules/preact/src/diff/catch-error.js","../node_modules/preact/src/component.js","../node_modules/preact/src/create-context.js","../src/defaults.ts","../src/util/registry.ts","../src/util/google-fonts.ts","../src/util/validate.ts","../src/transformer.ts","../src/util/process-fonts.ts","../src/emitter.ts"],"names":["slice","options","vnodeId","EMPTY_ARR","createElement","type","props","children","key","ref","i","normalizedProps","arguments","length","call","defaultProps","createVNode","original","vnode","__k","__","__b","__e","__c","constructor","__v","__i","__u","error","oldVNode","errorInfo","component","ctor","handled","getDerivedStateFromError","setState","__d","componentDidCatch","__E","e","Promise","prototype","then","bind","resolve","setTimeout","w","a","f","QUARTZ_DEFAULT_HEADER","QUARTZ_DEFAULT_BODY","QUARTZ_DEFAULT_CODE","defaultOptions"],"mappings":"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","file":"index.js","sourcesContent":["/** Normal hydration that attaches to a DOM tree but does not diff it. */\nexport const MODE_HYDRATE = 1 << 5;\n/** Signifies this VNode suspended on the previous render */\nexport const MODE_SUSPENDED = 1 << 7;\n/** Indicates that this node needs to be inserted while patching children */\nexport const INSERT_VNODE = 1 << 2;\n/** Indicates a VNode has been matched with another VNode in the diff */\nexport const MATCHED = 1 << 1;\n\n/** Reset all mode flags */\nexport const RESET_MODE = ~(MODE_HYDRATE | MODE_SUSPENDED);\n\nexport const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\nexport const XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\nexport const MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n\nexport const NULL = null;\nexport const UNDEFINED = undefined;\nexport const EMPTY_OBJ = /** @type {any} */ ({});\nexport const EMPTY_ARR = [];\nexport const IS_NON_DIMENSIONAL =\n\t/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;\n","import { EMPTY_ARR } from './constants';\n\nexport const isArray = Array.isArray;\n\n/**\n * Assign properties from `props` to `obj`\n * @template O, P The obj and props types\n * @param {O} obj The object to copy properties to\n * @param {P} props The object to copy properties from\n * @returns {O & P}\n */\nexport function assign(obj, props) {\n\t// @ts-expect-error We change the type of `obj` to be `O & P`\n\tfor (let i in props) obj[i] = props[i];\n\treturn /** @type {O & P} */ (obj);\n}\n\n/**\n * Remove a child node from its parent if attached. This is a workaround for\n * IE11 which doesn't support `Element.prototype.remove()`. Using this function\n * is smaller than including a dedicated polyfill.\n * @param {import('./index').ContainerNode} node The node to remove\n */\nexport function removeNode(node) {\n\tif (node && node.parentNode) node.parentNode.removeChild(node);\n}\n\nexport const slice = EMPTY_ARR.slice;\n","import { _catchError } from './diff/catch-error';\n\n/**\n * The `option` object can potentially contain callback functions\n * that are called during various stages of our renderer. This is the\n * foundation on which all our addons like `preact/debug`, `preact/compat`,\n * and `preact/hooks` are based on. See the `Options` type in `internal.d.ts`\n * for a full list of available option hooks (most editors/IDEs allow you to\n * ctrl+click or cmd+click on mac the type definition below).\n * @type {import('./internal').Options}\n */\nconst options = {\n\t_catchError\n};\n\nexport default options;\n","import { slice } from './util';\nimport options from './options';\nimport { NULL, UNDEFINED } from './constants';\n\nlet vnodeId = 0;\n\n/**\n * Create an virtual node (used for JSX)\n * @param {import('./internal').VNode[\"type\"]} type The node name or Component constructor for this\n * virtual node\n * @param {object | null | undefined} [props] The properties of the virtual node\n * @param {Array} [children] The children of the\n * virtual node\n * @returns {import('./internal').VNode}\n */\nexport function createElement(type, props, children) {\n\tlet normalizedProps = {},\n\t\tkey,\n\t\tref,\n\t\ti;\n\tfor (i in props) {\n\t\tif (i == 'key') key = props[i];\n\t\telse if (i == 'ref') ref = props[i];\n\t\telse normalizedProps[i] = props[i];\n\t}\n\n\tif (arguments.length > 2) {\n\t\tnormalizedProps.children =\n\t\t\targuments.length > 3 ? slice.call(arguments, 2) : children;\n\t}\n\n\t// If a Component VNode, check for and apply defaultProps\n\t// Note: type may be undefined in development, must never error here.\n\tif (typeof type == 'function' && type.defaultProps != NULL) {\n\t\tfor (i in type.defaultProps) {\n\t\t\tif (normalizedProps[i] === UNDEFINED) {\n\t\t\t\tnormalizedProps[i] = type.defaultProps[i];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn createVNode(type, normalizedProps, key, ref, NULL);\n}\n\n/**\n * Create a VNode (used internally by Preact)\n * @param {import('./internal').VNode[\"type\"]} type The node name or Component\n * Constructor for this virtual node\n * @param {object | string | number | null} props The properties of this virtual node.\n * If this virtual node represents a text node, this is the text of the node (string or number).\n * @param {string | number | null} key The key for this virtual node, used when\n * diffing it against its children\n * @param {import('./internal').VNode[\"ref\"]} ref The ref property that will\n * receive a reference to its created child\n * @returns {import('./internal').VNode}\n */\nexport function createVNode(type, props, key, ref, original) {\n\t// V8 seems to be better at detecting type shapes if the object is allocated from the same call site\n\t// Do not inline into createElement and coerceToVNode!\n\t/** @type {import('./internal').VNode} */\n\tconst vnode = {\n\t\ttype,\n\t\tprops,\n\t\tkey,\n\t\tref,\n\t\t_children: NULL,\n\t\t_parent: NULL,\n\t\t_depth: 0,\n\t\t_dom: NULL,\n\t\t_component: NULL,\n\t\tconstructor: UNDEFINED,\n\t\t_original: original == NULL ? ++vnodeId : original,\n\t\t_index: -1,\n\t\t_flags: 0\n\t};\n\n\t// Only invoke the vnode hook if this was *not* a direct copy:\n\tif (original == NULL && options.vnode != NULL) options.vnode(vnode);\n\n\treturn vnode;\n}\n\nexport function createRef() {\n\treturn { current: NULL };\n}\n\nexport function Fragment(props) {\n\treturn props.children;\n}\n\n/**\n * Check if a the argument is a valid Preact VNode.\n * @param {*} vnode\n * @returns {vnode is VNode}\n */\nexport const isValidElement = vnode =>\n\tvnode != NULL && vnode.constructor === UNDEFINED;\n","import { NULL } from '../constants';\n\n/**\n * Find the closest error boundary to a thrown error and call it\n * @param {object} error The thrown value\n * @param {import('../internal').VNode} vnode The vnode that threw the error that was caught (except\n * for unmounting when this parameter is the highest parent that was being\n * unmounted)\n * @param {import('../internal').VNode} [oldVNode]\n * @param {import('../internal').ErrorInfo} [errorInfo]\n */\nexport function _catchError(error, vnode, oldVNode, errorInfo) {\n\t/** @type {import('../internal').Component} */\n\tlet component,\n\t\t/** @type {import('../internal').ComponentType} */\n\t\tctor,\n\t\t/** @type {boolean} */\n\t\thandled;\n\n\tfor (; (vnode = vnode._parent); ) {\n\t\tif ((component = vnode._component) && !component._processingException) {\n\t\t\ttry {\n\t\t\t\tctor = component.constructor;\n\n\t\t\t\tif (ctor && ctor.getDerivedStateFromError != NULL) {\n\t\t\t\t\tcomponent.setState(ctor.getDerivedStateFromError(error));\n\t\t\t\t\thandled = component._dirty;\n\t\t\t\t}\n\n\t\t\t\tif (component.componentDidCatch != NULL) {\n\t\t\t\t\tcomponent.componentDidCatch(error, errorInfo || {});\n\t\t\t\t\thandled = component._dirty;\n\t\t\t\t}\n\n\t\t\t\t// This is an error boundary. Mark it as having bailed out, and whether it was mid-hydration.\n\t\t\t\tif (handled) {\n\t\t\t\t\treturn (component._pendingError = component);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\terror = e;\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow error;\n}\n","import { assign } from './util';\nimport { diff, commitRoot } from './diff/index';\nimport options from './options';\nimport { Fragment } from './create-element';\nimport { MODE_HYDRATE, NULL } from './constants';\n\n/**\n * Base Component class. Provides `setState()` and `forceUpdate()`, which\n * trigger rendering\n * @param {object} props The initial component props\n * @param {object} context The initial context from parent components'\n * getChildContext\n */\nexport function BaseComponent(props, context) {\n\tthis.props = props;\n\tthis.context = context;\n}\n\n/**\n * Update component state and schedule a re-render.\n * @this {import('./internal').Component}\n * @param {object | ((s: object, p: object) => object)} update A hash of state\n * properties to update with new values or a function that given the current\n * state and props returns a new partial state\n * @param {() => void} [callback] A function to be called once component state is\n * updated\n */\nBaseComponent.prototype.setState = function (update, callback) {\n\t// only clone state when copying to nextState the first time.\n\tlet s;\n\tif (this._nextState != NULL && this._nextState != this.state) {\n\t\ts = this._nextState;\n\t} else {\n\t\ts = this._nextState = assign({}, this.state);\n\t}\n\n\tif (typeof update == 'function') {\n\t\t// Some libraries like `immer` mark the current state as readonly,\n\t\t// preventing us from mutating it, so we need to clone it. See #2716\n\t\tupdate = update(assign({}, s), this.props);\n\t}\n\n\tif (update) {\n\t\tassign(s, update);\n\t}\n\n\t// Skip update if updater function returned null\n\tif (update == NULL) return;\n\n\tif (this._vnode) {\n\t\tif (callback) {\n\t\t\tthis._stateCallbacks.push(callback);\n\t\t}\n\t\tenqueueRender(this);\n\t}\n};\n\n/**\n * Immediately perform a synchronous re-render of the component\n * @this {import('./internal').Component}\n * @param {() => void} [callback] A function to be called after component is\n * re-rendered\n */\nBaseComponent.prototype.forceUpdate = function (callback) {\n\tif (this._vnode) {\n\t\t// Set render mode so that we can differentiate where the render request\n\t\t// is coming from. We need this because forceUpdate should never call\n\t\t// shouldComponentUpdate\n\t\tthis._force = true;\n\t\tif (callback) this._renderCallbacks.push(callback);\n\t\tenqueueRender(this);\n\t}\n};\n\n/**\n * Accepts `props` and `state`, and returns a new Virtual DOM tree to build.\n * Virtual DOM is generally constructed via [JSX](https://jasonformat.com/wtf-is-jsx).\n * @param {object} props Props (eg: JSX attributes) received from parent\n * element/component\n * @param {object} state The component's current state\n * @param {object} context Context object, as returned by the nearest\n * ancestor's `getChildContext()`\n * @returns {ComponentChildren | void}\n */\nBaseComponent.prototype.render = Fragment;\n\n/**\n * @param {import('./internal').VNode} vnode\n * @param {number | null} [childIndex]\n */\nexport function getDomSibling(vnode, childIndex) {\n\tif (childIndex == NULL) {\n\t\t// Use childIndex==null as a signal to resume the search from the vnode's sibling\n\t\treturn vnode._parent\n\t\t\t? getDomSibling(vnode._parent, vnode._index + 1)\n\t\t\t: NULL;\n\t}\n\n\tlet sibling;\n\tfor (; childIndex < vnode._children.length; childIndex++) {\n\t\tsibling = vnode._children[childIndex];\n\n\t\tif (sibling != NULL && sibling._dom != NULL) {\n\t\t\t// Since updateParentDomPointers keeps _dom pointer correct,\n\t\t\t// we can rely on _dom to tell us if this subtree contains a\n\t\t\t// rendered DOM node, and what the first rendered DOM node is\n\t\t\treturn sibling._dom;\n\t\t}\n\t}\n\n\t// If we get here, we have not found a DOM node in this vnode's children.\n\t// We must resume from this vnode's sibling (in it's parent _children array)\n\t// Only climb up and search the parent if we aren't searching through a DOM\n\t// VNode (meaning we reached the DOM parent of the original vnode that began\n\t// the search)\n\treturn typeof vnode.type == 'function' ? getDomSibling(vnode) : NULL;\n}\n\n/**\n * Trigger in-place re-rendering of a component.\n * @param {import('./internal').Component} component The component to rerender\n */\nfunction renderComponent(component) {\n\tlet oldVNode = component._vnode,\n\t\toldDom = oldVNode._dom,\n\t\tcommitQueue = [],\n\t\trefQueue = [];\n\n\tif (component._parentDom) {\n\t\tconst newVNode = assign({}, oldVNode);\n\t\tnewVNode._original = oldVNode._original + 1;\n\t\tif (options.vnode) options.vnode(newVNode);\n\n\t\tdiff(\n\t\t\tcomponent._parentDom,\n\t\t\tnewVNode,\n\t\t\toldVNode,\n\t\t\tcomponent._globalContext,\n\t\t\tcomponent._parentDom.namespaceURI,\n\t\t\toldVNode._flags & MODE_HYDRATE ? [oldDom] : NULL,\n\t\t\tcommitQueue,\n\t\t\toldDom == NULL ? getDomSibling(oldVNode) : oldDom,\n\t\t\t!!(oldVNode._flags & MODE_HYDRATE),\n\t\t\trefQueue\n\t\t);\n\n\t\tnewVNode._original = oldVNode._original;\n\t\tnewVNode._parent._children[newVNode._index] = newVNode;\n\t\tcommitRoot(commitQueue, newVNode, refQueue);\n\t\toldVNode._dom = oldVNode._parent = null;\n\n\t\tif (newVNode._dom != oldDom) {\n\t\t\tupdateParentDomPointers(newVNode);\n\t\t}\n\t}\n}\n\n/**\n * @param {import('./internal').VNode} vnode\n */\nfunction updateParentDomPointers(vnode) {\n\tif ((vnode = vnode._parent) != NULL && vnode._component != NULL) {\n\t\tvnode._dom = vnode._component.base = NULL;\n\t\tfor (let i = 0; i < vnode._children.length; i++) {\n\t\t\tlet child = vnode._children[i];\n\t\t\tif (child != NULL && child._dom != NULL) {\n\t\t\t\tvnode._dom = vnode._component.base = child._dom;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn updateParentDomPointers(vnode);\n\t}\n}\n\n/**\n * The render queue\n * @type {Array}\n */\nlet rerenderQueue = [];\n\n/*\n * The value of `Component.debounce` must asynchronously invoke the passed in callback. It is\n * important that contributors to Preact can consistently reason about what calls to `setState`, etc.\n * do, and when their effects will be applied. See the links below for some further reading on designing\n * asynchronous APIs.\n * * [Designing APIs for Asynchrony](https://blog.izs.me/2013/08/designing-apis-for-asynchrony)\n * * [Callbacks synchronous and asynchronous](https://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/)\n */\n\nlet prevDebounce;\n\nconst defer =\n\ttypeof Promise == 'function'\n\t\t? Promise.prototype.then.bind(Promise.resolve())\n\t\t: setTimeout;\n\n/**\n * Enqueue a rerender of a component\n * @param {import('./internal').Component} c The component to rerender\n */\nexport function enqueueRender(c) {\n\tif (\n\t\t(!c._dirty &&\n\t\t\t(c._dirty = true) &&\n\t\t\trerenderQueue.push(c) &&\n\t\t\t!process._rerenderCount++) ||\n\t\tprevDebounce != options.debounceRendering\n\t) {\n\t\tprevDebounce = options.debounceRendering;\n\t\t(prevDebounce || defer)(process);\n\t}\n}\n\n/**\n * @param {import('./internal').Component} a\n * @param {import('./internal').Component} b\n */\nconst depthSort = (a, b) => a._vnode._depth - b._vnode._depth;\n\n/** Flush the render queue by rerendering all queued components */\nfunction process() {\n\tlet c,\n\t\tl = 1;\n\n\t// Don't update `renderCount` yet. Keep its value non-zero to prevent unnecessary\n\t// process() calls from getting scheduled while `queue` is still being consumed.\n\twhile (rerenderQueue.length) {\n\t\t// Keep the rerender queue sorted by (depth, insertion order). The queue\n\t\t// will initially be sorted on the first iteration only if it has more than 1 item.\n\t\t//\n\t\t// New items can be added to the queue e.g. when rerendering a provider, so we want to\n\t\t// keep the order from top to bottom with those new items so we can handle them in a\n\t\t// single pass\n\t\tif (rerenderQueue.length > l) {\n\t\t\trerenderQueue.sort(depthSort);\n\t\t}\n\n\t\tc = rerenderQueue.shift();\n\t\tl = rerenderQueue.length;\n\n\t\tif (c._dirty) {\n\t\t\trenderComponent(c);\n\t\t}\n\t}\n\tprocess._rerenderCount = 0;\n}\n\nprocess._rerenderCount = 0;\n","import { enqueueRender } from './component';\nimport { NULL } from './constants';\n\nexport let i = 0;\n\nexport function createContext(defaultValue) {\n\tfunction Context(props) {\n\t\tif (!this.getChildContext) {\n\t\t\t/** @type {Set | null} */\n\t\t\tlet subs = new Set();\n\t\t\tlet ctx = {};\n\t\t\tctx[Context._id] = this;\n\n\t\t\tthis.getChildContext = () => ctx;\n\n\t\t\tthis.componentWillUnmount = () => {\n\t\t\t\tsubs = NULL;\n\t\t\t};\n\n\t\t\tthis.shouldComponentUpdate = function (_props) {\n\t\t\t\t// @ts-expect-error even\n\t\t\t\tif (this.props.value != _props.value) {\n\t\t\t\t\tsubs.forEach(c => {\n\t\t\t\t\t\tc._force = true;\n\t\t\t\t\t\tenqueueRender(c);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tthis.sub = c => {\n\t\t\t\tsubs.add(c);\n\t\t\t\tlet old = c.componentWillUnmount;\n\t\t\t\tc.componentWillUnmount = () => {\n\t\t\t\t\tif (subs) {\n\t\t\t\t\t\tsubs.delete(c);\n\t\t\t\t\t}\n\t\t\t\t\tif (old) old.call(c);\n\t\t\t\t};\n\t\t\t};\n\t\t}\n\n\t\treturn props.children;\n\t}\n\n\tContext._id = '__cC' + i++;\n\tContext._defaultValue = defaultValue;\n\n\t/** @type {import('./internal').FunctionComponent} */\n\tContext.Consumer = (props, contextValue) => {\n\t\treturn props.children(contextValue);\n\t};\n\n\t// we could also get rid of _contextRef entirely\n\tContext.Provider =\n\t\tContext._contextRef =\n\t\tContext.Consumer.contextType =\n\t\t\tContext;\n\n\treturn Context;\n}\n","export const OBSIDIAN_SANS_STACK =\n 'ui-sans-serif, -apple-system, BlinkMacSystemFont, system-ui, \"Segoe UI\", Roboto, \"Inter\", \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", sans-serif';\n\nexport const OBSIDIAN_MONO_STACK =\n 'ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Roboto Mono\", \"DejaVu Sans Mono\", \"Liberation Mono\", Menlo, Monaco, \"Consolas\", \"Source Code Pro\", monospace';\n\nexport type FontRole = \"title\" | \"header\" | \"body\" | \"code\";\n\nexport const DEFAULT_WEIGHTS: Record = {\n title: [400, 700],\n header: [400, 700],\n body: [400, 600],\n code: [400, 600],\n};\n\nexport const DEFAULT_ITALIC: Record = {\n title: false,\n header: false,\n body: true,\n code: false,\n};\n","import type { QuartzFontRegistry } from \"../types\";\n\nconst REGISTRY_KEY = \"__quartzFonts\";\n\nexport function readFontRegistry(): QuartzFontRegistry | undefined {\n const registry = (globalThis as Record)[REGISTRY_KEY];\n if (registry && typeof registry === \"object\" && \"themeName\" in registry && \"fonts\" in registry) {\n return registry as QuartzFontRegistry;\n }\n return undefined;\n}\n\nexport function isQuartzThemeEnabled(): boolean {\n return REGISTRY_KEY in (globalThis as Record);\n}\n","import type { FontSpecification } from \"../types\";\nimport type { FontRole } from \"../defaults\";\nimport { DEFAULT_WEIGHTS, DEFAULT_ITALIC } from \"../defaults\";\n\nfunction normalizeFontSpec(spec: FontSpecification): {\n name: string;\n weights?: number[];\n includeItalic?: boolean;\n} {\n return typeof spec === \"string\" ? { name: spec } : spec;\n}\n\nexport function getFontName(spec: FontSpecification): string {\n return typeof spec === \"string\" ? spec : spec.name;\n}\n\nexport function formatFontSpecification(role: FontRole, spec: FontSpecification): string {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const features: string[] = [];\n if (italic) {\n features.push(\"ital\");\n }\n\n if (resolvedWeights.length > 1) {\n const weightSpec = italic\n ? resolvedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : resolvedWeights.join(\";\");\n\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${name}:${features.join(\",\")}`;\n }\n\n return name;\n}\n\ninterface MergedFontEntry {\n name: string;\n weights: Set;\n italic: boolean;\n}\n\nfunction mergeInto(\n map: Map,\n role: FontRole,\n spec: FontSpecification,\n): void {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const existing = map.get(name);\n if (existing) {\n for (const w of resolvedWeights) existing.weights.add(w);\n if (italic) existing.italic = true;\n } else {\n map.set(name, { name, weights: new Set(resolvedWeights), italic });\n }\n}\n\nfunction formatMergedEntry(entry: MergedFontEntry): string {\n const sortedWeights = [...entry.weights].sort((a, b) => a - b);\n const features: string[] = [];\n\n if (entry.italic) {\n features.push(\"ital\");\n }\n\n if (sortedWeights.length > 1) {\n const weightSpec = entry.italic\n ? sortedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : sortedWeights.join(\";\");\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${entry.name}:${features.join(\",\")}`;\n }\n return entry.name;\n}\n\nexport function googleFontHref(fonts: {\n title?: FontSpecification;\n header: FontSpecification;\n body: FontSpecification;\n code: FontSpecification;\n}): string {\n const merged = new Map();\n\n mergeInto(merged, \"header\", fonts.header);\n mergeInto(merged, \"body\", fonts.body);\n mergeInto(merged, \"code\", fonts.code);\n\n if (fonts.title) {\n mergeInto(merged, \"title\", fonts.title);\n }\n\n const families = [...merged.values()].map(formatMergedEntry);\n const params = families.map((f) => `family=${encodeURIComponent(f)}`).join(\"&\");\n return `https://fonts.googleapis.com/css2?${params}&display=swap`;\n}\n","import type { FontSpecification } from \"../types\";\nimport type { FontRole } from \"../defaults\";\nimport { DEFAULT_WEIGHTS, DEFAULT_ITALIC } from \"../defaults\";\n\ninterface GoogleFontEntry {\n family: string;\n id: string;\n weights: number[];\n styles: string[];\n subsets: string[];\n}\n\ntype FontMetadata = Record;\n\nlet metadataCache: FontMetadata | null | false = null;\n\nfunction loadMetadata(): FontMetadata | undefined {\n if (metadataCache === false) return undefined;\n if (metadataCache) return metadataCache;\n\n try {\n // `require` is injected by tsup's banner via createRequire(import.meta.url).\n // Using it directly avoids a duplicate createRequire import in the bundle.\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { APIv1 } = require(\"google-font-metadata\") as { APIv1: FontMetadata };\n metadataCache = APIv1;\n return metadataCache;\n } catch {\n metadataCache = false;\n return undefined;\n }\n}\n\nfunction fontToId(family: string): string {\n return family.toLowerCase().replace(/\\s+/g, \"-\");\n}\n\nexport interface ValidationWarning {\n font: string;\n role: string;\n message: string;\n}\n\nexport function validateFontSpec(\n role: string,\n spec: FontSpecification,\n fontRole: FontRole,\n): ValidationWarning[] {\n const metadata = loadMetadata();\n if (!metadata) return [];\n\n const warnings: ValidationWarning[] = [];\n const name = typeof spec === \"string\" ? spec : spec.name;\n const id = fontToId(name);\n const entry = metadata[id];\n\n if (!entry) {\n warnings.push({\n font: name,\n role,\n message: `\"${name}\" is not a recognized Google Font. It will be requested but may fail to load.`,\n });\n return warnings;\n }\n\n const weights =\n typeof spec === \"object\" && spec.weights ? spec.weights : DEFAULT_WEIGHTS[fontRole];\n const italic =\n typeof spec === \"object\" && spec.includeItalic !== undefined\n ? spec.includeItalic\n : DEFAULT_ITALIC[fontRole];\n\n for (const weight of weights) {\n if (!entry.weights.includes(weight)) {\n warnings.push({\n font: name,\n role,\n message: `\"${name}\" does not support weight ${weight}. Available weights: ${entry.weights.join(\", \")}.`,\n });\n }\n }\n\n if (italic && !entry.styles.includes(\"italic\")) {\n warnings.push({\n font: name,\n role,\n message: `\"${name}\" does not support italic style. Available styles: ${entry.styles.join(\", \")}.`,\n });\n }\n\n return warnings;\n}\n","import { h } from \"preact\";\nimport type { QuartzTransformerPlugin, CSSResource } from \"@quartz-community/types\";\nimport type { FontSpecification, FontsOptions } from \"./types\";\nimport { OBSIDIAN_SANS_STACK, OBSIDIAN_MONO_STACK } from \"./defaults\";\nimport { readFontRegistry, isQuartzThemeEnabled } from \"./util/registry\";\nimport { googleFontHref } from \"./util/google-fonts\";\nimport { validateFontSpec } from \"./util/validate\";\n\nconst QUARTZ_DEFAULT_HEADER = \"Schibsted Grotesk\";\nconst QUARTZ_DEFAULT_BODY = \"Source Sans Pro\";\nconst QUARTZ_DEFAULT_CODE = \"IBM Plex Mono\";\n\nconst defaultOptions: FontsOptions = {\n useThemeFonts: true,\n fontOrigin: \"googleFonts\",\n};\n\nfunction toFontFamily(spec: FontSpecification): string {\n if (typeof spec === \"string\") return spec;\n return spec.name;\n}\n\ninterface ResolvedFonts {\n title: string;\n body: string;\n header: string;\n code: string;\n interface: string;\n h1: string;\n h2: string;\n h3: string;\n h4: string;\n h5: string;\n h6: string;\n}\n\nfunction resolveFonts(options: FontsOptions): ResolvedFonts {\n const registry = options.useThemeFonts !== false ? readFontRegistry() : undefined;\n\n if (options.useThemeFonts !== false && !registry) {\n if (isQuartzThemeEnabled()) {\n console.warn(\n \"[Fonts] QuartzTheme is enabled but its font registry is empty. \" +\n \"Ensure QuartzTheme runs before Fonts (lower defaultOrder number). \" +\n \"Falling back to Obsidian defaults.\",\n );\n }\n }\n\n const themeFonts = registry?.fonts ?? {};\n\n const resolve = (\n spec: FontSpecification | undefined,\n ...fallbacks: (string | undefined)[]\n ): string => {\n if (spec !== undefined) return toFontFamily(spec);\n for (const fb of fallbacks) {\n if (fb !== undefined) return fb;\n }\n return OBSIDIAN_SANS_STACK;\n };\n\n const body = resolve(\n options.body,\n themeFonts[\"--font-text\"],\n QUARTZ_DEFAULT_BODY,\n OBSIDIAN_SANS_STACK,\n );\n const header = resolve(\n options.header,\n themeFonts[\"--font-text\"],\n QUARTZ_DEFAULT_HEADER,\n OBSIDIAN_SANS_STACK,\n );\n const code = resolve(\n options.code,\n themeFonts[\"--font-monospace\"],\n QUARTZ_DEFAULT_CODE,\n OBSIDIAN_MONO_STACK,\n );\n const interfaceFont = resolve(\n options.interface,\n themeFonts[\"--font-interface\"],\n OBSIDIAN_SANS_STACK,\n );\n const title = resolve(options.title, undefined, header);\n\n const resolveHeading = (level: 1 | 2 | 3 | 4 | 5 | 6): string => {\n const levelKey = `h${level}` as keyof FontsOptions;\n const themeVarKey = `--h${level}-font`;\n const userSpec = options[levelKey] as FontSpecification | undefined;\n\n if (userSpec !== undefined) return toFontFamily(userSpec);\n if (themeFonts[themeVarKey]) return themeFonts[themeVarKey];\n if (options.header !== undefined) return toFontFamily(options.header);\n if (themeFonts[\"--font-text\"]) return themeFonts[\"--font-text\"];\n return header;\n };\n\n return {\n title,\n body,\n header,\n code,\n interface: interfaceFont,\n h1: resolveHeading(1),\n h2: resolveHeading(2),\n h3: resolveHeading(3),\n h4: resolveHeading(4),\n h5: resolveHeading(5),\n h6: resolveHeading(6),\n };\n}\n\nfunction runValidation(options: FontsOptions): void {\n const specs: Array<{\n role: string;\n spec: FontSpecification;\n fontRole: \"title\" | \"header\" | \"body\" | \"code\";\n }> = [];\n\n if (options.title) specs.push({ role: \"title\", spec: options.title, fontRole: \"title\" });\n if (options.header) specs.push({ role: \"header\", spec: options.header, fontRole: \"header\" });\n if (options.body) specs.push({ role: \"body\", spec: options.body, fontRole: \"body\" });\n if (options.code) specs.push({ role: \"code\", spec: options.code, fontRole: \"code\" });\n\n const headingLevels = [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"] as const;\n for (const level of headingLevels) {\n const spec = options[level];\n if (spec) specs.push({ role: level, spec, fontRole: \"header\" });\n }\n\n for (const { role, spec, fontRole } of specs) {\n const warnings = validateFontSpec(role, spec, fontRole);\n for (const w of warnings) {\n console.warn(`[Fonts] ${w.role}: ${w.message}`);\n }\n }\n}\n\nfunction buildLayeredCSS(fonts: ResolvedFonts): string {\n return [\n \"@layer quartz-fonts {\",\n \" :root {\",\n ` --titleFont: ${fonts.title};`,\n ` --bodyFont: ${fonts.body};`,\n ` --headerFont: ${fonts.header};`,\n ` --codeFont: ${fonts.code};`,\n ` --font-text: ${fonts.body};`,\n ` --font-interface: ${fonts.interface};`,\n ` --font-monospace: ${fonts.code};`,\n ` --h1-font: ${fonts.h1};`,\n ` --h2-font: ${fonts.h2};`,\n ` --h3-font: ${fonts.h3};`,\n ` --h4-font: ${fonts.h4};`,\n ` --h5-font: ${fonts.h5};`,\n ` --h6-font: ${fonts.h6};`,\n \" }\",\n \"}\",\n ].join(\"\\n\");\n}\n\nfunction buildUnlayeredCSS(fonts: ResolvedFonts): string {\n return [\n `h1 { font-family: ${fonts.h1}; }`,\n `h2 { font-family: ${fonts.h2}; }`,\n `h3 { font-family: ${fonts.h3}; }`,\n `h4 { font-family: ${fonts.h4}; }`,\n `h5 { font-family: ${fonts.h5}; }`,\n `h6 { font-family: ${fonts.h6}; }`,\n ].join(\"\\n\");\n}\n\nfunction buildGoogleFontsHead(options: FontsOptions): unknown[] {\n const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;\n const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;\n const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;\n\n const href = googleFontHref({\n title: options.title,\n header: headerSpec,\n body: bodySpec,\n code: codeSpec,\n });\n\n return [\n h(\"link\", { rel: \"preconnect\", href: \"https://fonts.googleapis.com\" }),\n h(\"link\", { rel: \"preconnect\", href: \"https://fonts.gstatic.com\", crossOrigin: \"anonymous\" }),\n h(\"link\", { rel: \"stylesheet\", href }),\n ];\n}\n\nexport const Fonts: QuartzTransformerPlugin> = (\n userOptions?: Partial,\n) => {\n const options: FontsOptions = { ...defaultOptions, ...userOptions };\n\n if (options.fontOrigin === \"googleFonts\") {\n runValidation(options);\n }\n\n return {\n name: \"Fonts\",\n textTransform(_ctx, src) {\n return src;\n },\n externalResources(_ctx) {\n const fonts = resolveFonts(options);\n\n const css: CSSResource[] = [\n { content: buildLayeredCSS(fonts), inline: true },\n { content: buildUnlayeredCSS(fonts), inline: true },\n ];\n\n let additionalHead: unknown[] = [];\n if (options.fontOrigin === \"googleFonts\") {\n additionalHead = buildGoogleFontsHead(options);\n } else if (options.fontOrigin === \"selfHosted\") {\n additionalHead = [h(\"link\", { rel: \"stylesheet\", href: \"/static/fonts/quartz-fonts.css\" })];\n }\n\n return { css, js: [], additionalHead };\n },\n };\n};\n","import type { ProcessedFontResult, GoogleFontFile } from \"../types\";\n\nconst fontMimeMap: Record = {\n truetype: \"ttf\",\n woff: \"woff\",\n woff2: \"woff2\",\n opentype: \"otf\",\n};\n\nconst fontUrlPattern =\n /url\\((https:\\/\\/fonts.gstatic.com\\/.+(?:\\/|(?:kit=))(.+?)[.&].+?)\\)\\sformat\\('(\\w+?)'\\);/g;\n\nexport function processGoogleFonts(stylesheet: string, baseUrl: string): ProcessedFontResult {\n const fontFiles: GoogleFontFile[] = [];\n\n const processedStylesheet = stylesheet.replace(\n fontUrlPattern,\n (match, url: string, filename: string, format: string) => {\n const extension = fontMimeMap[format] ?? format;\n const rewrittenUrl = `https://${baseUrl}/static/fonts/${filename}.${extension}`;\n fontFiles.push({ url, filename, extension });\n return match.replace(url, rewrittenUrl);\n },\n );\n\n return { processedStylesheet, fontFiles };\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport type { QuartzEmitterPlugin, BuildCtx, FilePath } from \"@quartz-community/types\";\nimport type { FontsOptions } from \"./types\";\nimport { googleFontHref } from \"./util/google-fonts\";\nimport { processGoogleFonts } from \"./util/process-fonts\";\n\nconst QUARTZ_DEFAULT_HEADER = \"Schibsted Grotesk\";\nconst QUARTZ_DEFAULT_BODY = \"Source Sans Pro\";\nconst QUARTZ_DEFAULT_CODE = \"IBM Plex Mono\";\n\nconst defaultOptions: FontsOptions = {\n useThemeFonts: true,\n fontOrigin: \"googleFonts\",\n};\n\nexport const FontsEmitter: QuartzEmitterPlugin> = (\n userOptions?: Partial,\n) => {\n const options: FontsOptions = { ...defaultOptions, ...userOptions };\n\n return {\n name: \"FontsEmitter\",\n async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator {\n if (options.fontOrigin !== \"selfHosted\") {\n return;\n }\n\n const baseUrl = ctx.cfg.configuration.baseUrl;\n if (!baseUrl) {\n throw new Error(\"[FontsEmitter] baseUrl is required for selfHosted fonts.\");\n }\n\n const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;\n const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;\n const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;\n\n const href = googleFontHref({\n title: options.title,\n header: headerSpec,\n body: bodySpec,\n code: codeSpec,\n });\n\n const cssResponse = await fetch(href);\n if (!cssResponse.ok) {\n throw new Error(\n `[FontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`,\n );\n }\n const cssText = await cssResponse.text();\n\n const { processedStylesheet, fontFiles } = processGoogleFonts(cssText, baseUrl);\n\n const fontsDir = path.join(ctx.argv.output, \"static\", \"fonts\");\n await fs.promises.mkdir(fontsDir, { recursive: true });\n\n for (const fontFile of fontFiles) {\n const fontResponse = await fetch(fontFile.url);\n if (!fontResponse.ok) {\n throw new Error(\n `[FontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`,\n );\n }\n const buf = await fontResponse.arrayBuffer();\n const filePath = path.join(fontsDir, `${fontFile.filename}.${fontFile.extension}`);\n await fs.promises.writeFile(filePath, Buffer.from(buf));\n yield filePath as FilePath;\n }\n\n const cssPath = path.join(fontsDir, \"quartz-fonts.css\");\n await fs.promises.writeFile(cssPath, processedStylesheet);\n yield cssPath as FilePath;\n },\n };\n};\n"]} \ No newline at end of file diff --git a/dist/types.d.ts b/dist/types.d.ts index 3afe33e..2bebccd 100644 --- a/dist/types.d.ts +++ b/dist/types.d.ts @@ -9,7 +9,7 @@ type FontSpecification = string | { weights?: number[]; includeItalic?: boolean; }; -interface QuartzFontsOptions { +interface FontsOptions { title?: FontSpecification; body?: FontSpecification; header?: FontSpecification; @@ -55,4 +55,4 @@ interface FontFileEntry { unicodeRange?: string | null; } -export type { FontFileEntry, FontSpecification, GoogleFontFile, ProcessedFontResult, QuartzFontRegistry, QuartzFontsOptions }; +export type { FontFileEntry, FontSpecification, FontsOptions, GoogleFontFile, ProcessedFontResult, QuartzFontRegistry }; diff --git a/package.json b/package.json index c7907f1..893a879 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "homepage": "https://quartz.jzhao.xyz", "repository": { "type": "git", - "url": "https://github.com/quartz-community/quartz-fonts" + "url": "https://github.com/quartz-community/fonts" }, "keywords": [ "quartz", @@ -86,8 +86,8 @@ "vitest": "^2.1.9" }, "quartz": { - "name": "quartz-fonts", - "displayName": "Quartz Fonts", + "name": "fonts", + "displayName": "Fonts", "category": [ "transformer", "emitter" diff --git a/src/emitter.ts b/src/emitter.ts index 91e173d..e6b34c4 100644 --- a/src/emitter.ts +++ b/src/emitter.ts @@ -1,7 +1,7 @@ import fs from "fs"; import path from "path"; import type { QuartzEmitterPlugin, BuildCtx, FilePath } from "@quartz-community/types"; -import type { QuartzFontsOptions } from "./types"; +import type { FontsOptions } from "./types"; import { googleFontHref } from "./util/google-fonts"; import { processGoogleFonts } from "./util/process-fonts"; @@ -9,18 +9,18 @@ const QUARTZ_DEFAULT_HEADER = "Schibsted Grotesk"; const QUARTZ_DEFAULT_BODY = "Source Sans Pro"; const QUARTZ_DEFAULT_CODE = "IBM Plex Mono"; -const defaultOptions: QuartzFontsOptions = { +const defaultOptions: FontsOptions = { useThemeFonts: true, fontOrigin: "googleFonts", }; -export const QuartzFontsEmitter: QuartzEmitterPlugin> = ( - userOptions?: Partial, +export const FontsEmitter: QuartzEmitterPlugin> = ( + userOptions?: Partial, ) => { - const options: QuartzFontsOptions = { ...defaultOptions, ...userOptions }; + const options: FontsOptions = { ...defaultOptions, ...userOptions }; return { - name: "QuartzFontsEmitter", + name: "FontsEmitter", async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator { if (options.fontOrigin !== "selfHosted") { return; @@ -28,7 +28,7 @@ export const QuartzFontsEmitter: QuartzEmitterPlugin const baseUrl = ctx.cfg.configuration.baseUrl; if (!baseUrl) { - throw new Error("[QuartzFontsEmitter] baseUrl is required for selfHosted fonts."); + throw new Error("[FontsEmitter] baseUrl is required for selfHosted fonts."); } const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER; @@ -45,7 +45,7 @@ export const QuartzFontsEmitter: QuartzEmitterPlugin const cssResponse = await fetch(href); if (!cssResponse.ok) { throw new Error( - `[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`, + `[FontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`, ); } const cssText = await cssResponse.text(); @@ -59,7 +59,7 @@ export const QuartzFontsEmitter: QuartzEmitterPlugin const fontResponse = await fetch(fontFile.url); if (!fontResponse.ok) { throw new Error( - `[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`, + `[FontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`, ); } const buf = await fontResponse.arrayBuffer(); diff --git a/src/index.ts b/src/index.ts index e30b11a..2c8ff57 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,9 @@ -export { QuartzFonts } from "./transformer"; -export { QuartzFontsEmitter } from "./emitter"; +export { Fonts } from "./transformer"; +export { FontsEmitter } from "./emitter"; export type { FontSpecification, - QuartzFontsOptions, + FontsOptions, QuartzFontRegistry, FontFileEntry, ProcessedFontResult, diff --git a/src/transformer.ts b/src/transformer.ts index 3d3127e..b4f628f 100644 --- a/src/transformer.ts +++ b/src/transformer.ts @@ -1,6 +1,6 @@ import { h } from "preact"; import type { QuartzTransformerPlugin, CSSResource } from "@quartz-community/types"; -import type { FontSpecification, QuartzFontsOptions } from "./types"; +import type { FontSpecification, FontsOptions } from "./types"; import { OBSIDIAN_SANS_STACK, OBSIDIAN_MONO_STACK } from "./defaults"; import { readFontRegistry, isQuartzThemeEnabled } from "./util/registry"; import { googleFontHref } from "./util/google-fonts"; @@ -10,7 +10,7 @@ const QUARTZ_DEFAULT_HEADER = "Schibsted Grotesk"; const QUARTZ_DEFAULT_BODY = "Source Sans Pro"; const QUARTZ_DEFAULT_CODE = "IBM Plex Mono"; -const defaultOptions: QuartzFontsOptions = { +const defaultOptions: FontsOptions = { useThemeFonts: true, fontOrigin: "googleFonts", }; @@ -34,14 +34,14 @@ interface ResolvedFonts { h6: string; } -function resolveFonts(options: QuartzFontsOptions): ResolvedFonts { +function resolveFonts(options: FontsOptions): ResolvedFonts { const registry = options.useThemeFonts !== false ? readFontRegistry() : undefined; if (options.useThemeFonts !== false && !registry) { if (isQuartzThemeEnabled()) { console.warn( - "[QuartzFonts] QuartzTheme is enabled but its font registry is empty. " + - "Ensure QuartzTheme runs before QuartzFonts (lower defaultOrder number). " + + "[Fonts] QuartzTheme is enabled but its font registry is empty. " + + "Ensure QuartzTheme runs before Fonts (lower defaultOrder number). " + "Falling back to Obsidian defaults.", ); } @@ -86,7 +86,7 @@ function resolveFonts(options: QuartzFontsOptions): ResolvedFonts { const title = resolve(options.title, undefined, header); const resolveHeading = (level: 1 | 2 | 3 | 4 | 5 | 6): string => { - const levelKey = `h${level}` as keyof QuartzFontsOptions; + const levelKey = `h${level}` as keyof FontsOptions; const themeVarKey = `--h${level}-font`; const userSpec = options[levelKey] as FontSpecification | undefined; @@ -112,7 +112,7 @@ function resolveFonts(options: QuartzFontsOptions): ResolvedFonts { }; } -function runValidation(options: QuartzFontsOptions): void { +function runValidation(options: FontsOptions): void { const specs: Array<{ role: string; spec: FontSpecification; @@ -133,7 +133,7 @@ function runValidation(options: QuartzFontsOptions): void { for (const { role, spec, fontRole } of specs) { const warnings = validateFontSpec(role, spec, fontRole); for (const w of warnings) { - console.warn(`[QuartzFonts] ${w.role}: ${w.message}`); + console.warn(`[Fonts] ${w.role}: ${w.message}`); } } } @@ -171,7 +171,7 @@ function buildUnlayeredCSS(fonts: ResolvedFonts): string { ].join("\n"); } -function buildGoogleFontsHead(options: QuartzFontsOptions): unknown[] { +function buildGoogleFontsHead(options: FontsOptions): unknown[] { const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER; const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY; const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE; @@ -190,17 +190,17 @@ function buildGoogleFontsHead(options: QuartzFontsOptions): unknown[] { ]; } -export const QuartzFonts: QuartzTransformerPlugin> = ( - userOptions?: Partial, +export const Fonts: QuartzTransformerPlugin> = ( + userOptions?: Partial, ) => { - const options: QuartzFontsOptions = { ...defaultOptions, ...userOptions }; + const options: FontsOptions = { ...defaultOptions, ...userOptions }; if (options.fontOrigin === "googleFonts") { runValidation(options); } return { - name: "QuartzFonts", + name: "Fonts", textTransform(_ctx, src) { return src; }, diff --git a/src/types.ts b/src/types.ts index 9fa74ca..7c7f3a0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,7 +19,7 @@ export type FontSpecification = includeItalic?: boolean; }; -export interface QuartzFontsOptions { +export interface FontsOptions { title?: FontSpecification; body?: FontSpecification; header?: FontSpecification; diff --git a/test/transformer.test.ts b/test/transformer.test.ts index 64637cc..496ece9 100644 --- a/test/transformer.test.ts +++ b/test/transformer.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, beforeEach, afterEach } from "vitest"; import type { BuildCtx } from "@quartz-community/types"; -import { QuartzFonts } from "../src/transformer"; +import { Fonts } from "../src/transformer"; import { formatFontSpecification, googleFontHref, getFontName } from "../src/util/google-fonts"; import { processGoogleFonts } from "../src/util/process-fonts"; @@ -17,24 +17,24 @@ function clearRegistry() { delete (globalThis as Record)[REGISTRY_KEY]; } -function getCSSContent(plugin: ReturnType): string { +function getCSSContent(plugin: ReturnType): string { const resources = plugin.externalResources!(mockCtx); return (resources?.css ?? []).map((r) => ("content" in r ? r.content : "")).join("\n"); } -function getAdditionalHead(plugin: ReturnType): unknown[] { +function getAdditionalHead(plugin: ReturnType): unknown[] { const resources = plugin.externalResources!(mockCtx); return (resources as { additionalHead?: unknown[] })?.additionalHead ?? []; } -describe("QuartzFonts", () => { +describe("Fonts", () => { afterEach(() => { clearRegistry(); }); describe("standalone (no QuartzTheme)", () => { it("uses Quartz defaults when no options provided", () => { - const css = getCSSContent(QuartzFonts()); + const css = getCSSContent(Fonts()); expect(css).toContain("--bodyFont: Source Sans Pro"); expect(css).toContain("--codeFont: IBM Plex Mono"); @@ -42,14 +42,14 @@ describe("QuartzFonts", () => { }); it("applies user-provided body font", () => { - const css = getCSSContent(QuartzFonts({ body: '"Inter", sans-serif' })); + const css = getCSSContent(Fonts({ body: '"Inter", sans-serif' })); expect(css).toContain('--bodyFont: "Inter", sans-serif'); expect(css).toContain('--font-text: "Inter", sans-serif'); }); it("applies user-provided header font to all headings", () => { - const css = getCSSContent(QuartzFonts({ header: '"Playfair Display", serif' })); + const css = getCSSContent(Fonts({ header: '"Playfair Display", serif' })); expect(css).toContain('--h1-font: "Playfair Display", serif'); expect(css).toContain('--h6-font: "Playfair Display", serif'); @@ -57,7 +57,7 @@ describe("QuartzFonts", () => { it("applies per-heading overrides", () => { const css = getCSSContent( - QuartzFonts({ + Fonts({ header: '"Lora", serif', h1: '"Playfair Display", serif', h3: '"Inter", sans-serif', @@ -70,7 +70,7 @@ describe("QuartzFonts", () => { }); it("emits unlayered heading CSS", () => { - const plugin = QuartzFonts({ header: '"Lora", serif' }); + const plugin = Fonts({ header: '"Lora", serif' }); const resources = plugin.externalResources!(mockCtx); const unlayeredCSS = (resources?.css ?? []) .map((r) => ("content" in r ? r.content : "")) @@ -84,25 +84,25 @@ describe("QuartzFonts", () => { describe("title font", () => { it("defaults title to header value", () => { - const css = getCSSContent(QuartzFonts({ header: '"Lora", serif' })); + const css = getCSSContent(Fonts({ header: '"Lora", serif' })); expect(css).toContain('--titleFont: "Lora", serif'); }); it("defaults title to header default when no header set", () => { - const css = getCSSContent(QuartzFonts()); + const css = getCSSContent(Fonts()); expect(css).toContain("--titleFont: Schibsted Grotesk"); }); it("applies explicit title font", () => { const css = getCSSContent( - QuartzFonts({ title: '"Abril Fatface", serif', header: '"Lora", serif' }), + Fonts({ title: '"Abril Fatface", serif', header: '"Lora", serif' }), ); expect(css).toContain('--titleFont: "Abril Fatface", serif'); expect(css).toContain('--headerFont: "Lora", serif'); }); it("accepts FontSpecification object for title", () => { - const css = getCSSContent(QuartzFonts({ title: { name: "Abril Fatface", weights: [400] } })); + const css = getCSSContent(Fonts({ title: { name: "Abril Fatface", weights: [400] } })); expect(css).toContain("--titleFont: Abril Fatface"); }); }); @@ -110,7 +110,7 @@ describe("QuartzFonts", () => { describe("FontSpecification object form", () => { it("extracts font name from object spec for CSS", () => { const css = getCSSContent( - QuartzFonts({ + Fonts({ body: { name: "Inter", weights: [400, 700], includeItalic: true }, header: { name: "Playfair Display", weights: [400, 700] }, code: { name: "JetBrains Mono", weights: [400] }, @@ -124,7 +124,7 @@ describe("QuartzFonts", () => { it("accepts object form for heading overrides", () => { const css = getCSSContent( - QuartzFonts({ + Fonts({ h1: { name: "Abril Fatface", weights: [400] }, h2: '"Lora", serif', }), @@ -137,13 +137,13 @@ describe("QuartzFonts", () => { describe("Google Fonts loading", () => { it("does not inject link tags when fontOrigin is local", () => { - const head = getAdditionalHead(QuartzFonts({ fontOrigin: "local", body: "Inter" })); + const head = getAdditionalHead(Fonts({ fontOrigin: "local", body: "Inter" })); expect(head).toHaveLength(0); }); it("injects preconnect and stylesheet VNodes for googleFonts", () => { const head = getAdditionalHead( - QuartzFonts({ + Fonts({ fontOrigin: "googleFonts", body: "Inter", header: "Playfair Display", @@ -170,7 +170,7 @@ describe("QuartzFonts", () => { it("includes title font in Google Fonts URL when different from header", () => { const head = getAdditionalHead( - QuartzFonts({ + Fonts({ fontOrigin: "googleFonts", title: "Abril Fatface", header: "Playfair Display", @@ -186,7 +186,7 @@ describe("QuartzFonts", () => { it("deduplicates title and header when they match", () => { const head = getAdditionalHead( - QuartzFonts({ + Fonts({ fontOrigin: "googleFonts", title: "Inter", header: "Inter", @@ -204,7 +204,7 @@ describe("QuartzFonts", () => { describe("selfHosted fontOrigin", () => { it("injects link to self-hosted CSS file instead of Google Fonts", () => { const head = getAdditionalHead( - QuartzFonts({ + Fonts({ fontOrigin: "selfHosted", body: "Inter", header: "Playfair Display", @@ -220,7 +220,7 @@ describe("QuartzFonts", () => { it("still emits layered CSS with font variables", () => { const css = getCSSContent( - QuartzFonts({ + Fonts({ fontOrigin: "selfHosted", body: "Inter", header: "Playfair Display", @@ -247,7 +247,7 @@ describe("QuartzFonts", () => { }); it("reads theme fonts from registry", () => { - const css = getCSSContent(QuartzFonts()); + const css = getCSSContent(Fonts()); expect(css).toContain('--bodyFont: "JetBrains Mono", monospace'); expect(css).toContain('--codeFont: "Fira Code", monospace'); @@ -255,14 +255,14 @@ describe("QuartzFonts", () => { }); it("user options override theme fonts", () => { - const css = getCSSContent(QuartzFonts({ body: '"Inter", sans-serif' })); + const css = getCSSContent(Fonts({ body: '"Inter", sans-serif' })); expect(css).toContain('--bodyFont: "Inter", sans-serif'); expect(css).toContain('--codeFont: "Fira Code", monospace'); }); it("useThemeFonts: false ignores registry", () => { - const css = getCSSContent(QuartzFonts({ useThemeFonts: false })); + const css = getCSSContent(Fonts({ useThemeFonts: false })); expect(css).toContain("--bodyFont: Source Sans Pro"); expect(css).not.toContain("JetBrains Mono"); @@ -271,12 +271,12 @@ describe("QuartzFonts", () => { describe("plugin metadata", () => { it("has the correct name", () => { - const plugin = QuartzFonts(); - expect(plugin.name).toBe("QuartzFonts"); + const plugin = Fonts(); + expect(plugin.name).toBe("Fonts"); }); it("textTransform passes through source unchanged", () => { - const plugin = QuartzFonts(); + const plugin = Fonts(); const input = "# Hello World\n"; const result = plugin.textTransform!(mockCtx, input); expect(result).toBe(input);