@quartz-community/quartz-fonts 0.1.1 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 707a13b: Use `configuration.theme.typography` as the fallback when no font options are set, instead of always applying the hard-coded Quartz default fonts (jackyzha0/quartz#2507).
8
+ - 707a13b: Self-hosted fonts now work on localhost and under a base path: font URLs in `quartz-fonts.css` are relative to the stylesheet and the stylesheet link is relative to each page (jackyzha0/quartz#2547). `baseUrl` is no longer required for `fontOrigin: "selfHosted"`.
9
+
10
+ ## 1.0.0
11
+
12
+ ### Major Changes
13
+
14
+ - Stable 1.0 release. All `@quartz-community/*` dependencies now use `^1.0.0` ranges.
15
+
16
+ Pre-1.0 caret ranges pinned the minor version (`^0.2.1` means `>=0.2.1 <0.3.0`), so
17
+ published fixes to shared packages could never be resolved by dependents. Moving the
18
+ ecosystem to 1.0 makes caret ranges behave conventionally.
19
+
3
20
  All notable changes to this project will be documented in this file.
4
21
 
5
22
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
package/dist/emitter.js CHANGED
@@ -70,13 +70,13 @@ var fontMimeMap = {
70
70
  opentype: "otf"
71
71
  };
72
72
  var fontUrlPattern = /url\((https:\/\/fonts.gstatic.com\/.+(?:\/|(?:kit=))(.+?)[.&].+?)\)\sformat\('(\w+?)'\);/g;
73
- function processGoogleFonts(stylesheet, baseUrl) {
73
+ function processGoogleFonts(stylesheet) {
74
74
  const fontFiles = [];
75
75
  const processedStylesheet = stylesheet.replace(
76
76
  fontUrlPattern,
77
77
  (match, url, filename, format) => {
78
78
  const extension = fontMimeMap[format] ?? format;
79
- const rewrittenUrl = `https://${baseUrl}/static/fonts/${filename}.${extension}`;
79
+ const rewrittenUrl = `${filename}.${extension}`;
80
80
  fontFiles.push({ url, filename, extension });
81
81
  return match.replace(url, rewrittenUrl);
82
82
  }
@@ -84,10 +84,35 @@ function processGoogleFonts(stylesheet, baseUrl) {
84
84
  return { processedStylesheet, fontFiles };
85
85
  }
86
86
 
87
- // src/emitter.ts
87
+ // src/util/typography.ts
88
88
  var QUARTZ_DEFAULT_HEADER = "Schibsted Grotesk";
89
89
  var QUARTZ_DEFAULT_BODY = "Source Sans Pro";
90
90
  var QUARTZ_DEFAULT_CODE = "IBM Plex Mono";
91
+ function isFontSpecification(value) {
92
+ if (typeof value === "string") return value.trim().length > 0;
93
+ return typeof value === "object" && value !== null && typeof value.name === "string";
94
+ }
95
+ function readConfiguredTypography(ctx) {
96
+ const theme = ctx?.cfg?.configuration?.theme;
97
+ const typography = theme?.typography;
98
+ if (typeof typography !== "object" || typography === null) return {};
99
+ const result = {};
100
+ for (const role of ["title", "header", "body", "code"]) {
101
+ const spec = typography[role];
102
+ if (isFontSpecification(spec)) result[role] = spec;
103
+ }
104
+ return result;
105
+ }
106
+ function resolveFontSpecs(options, typography) {
107
+ return {
108
+ title: options.title ?? typography.title,
109
+ header: options.header ?? typography.header ?? QUARTZ_DEFAULT_HEADER,
110
+ body: options.body ?? typography.body ?? QUARTZ_DEFAULT_BODY,
111
+ code: options.code ?? typography.code ?? QUARTZ_DEFAULT_CODE
112
+ };
113
+ }
114
+
115
+ // src/emitter.ts
91
116
  var defaultOptions = {
92
117
  useThemeFonts: true,
93
118
  fontOrigin: "googleFonts"
@@ -100,19 +125,7 @@ var FontsEmitter = (userOptions) => {
100
125
  if (options.fontOrigin !== "selfHosted") {
101
126
  return;
102
127
  }
103
- const baseUrl = ctx.cfg.configuration.baseUrl;
104
- if (!baseUrl) {
105
- throw new Error("[FontsEmitter] baseUrl is required for selfHosted fonts.");
106
- }
107
- const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;
108
- const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;
109
- const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;
110
- const href = googleFontHref({
111
- title: options.title,
112
- header: headerSpec,
113
- body: bodySpec,
114
- code: codeSpec
115
- });
128
+ const href = googleFontHref(resolveFontSpecs(options, readConfiguredTypography(ctx)));
116
129
  const cssResponse = await fetch(href);
117
130
  if (!cssResponse.ok) {
118
131
  throw new Error(
@@ -120,7 +133,7 @@ var FontsEmitter = (userOptions) => {
120
133
  );
121
134
  }
122
135
  const cssText = await cssResponse.text();
123
- const { processedStylesheet, fontFiles } = processGoogleFonts(cssText, baseUrl);
136
+ const { processedStylesheet, fontFiles } = processGoogleFonts(cssText);
124
137
  const fontsDir = path.join(ctx.argv.output, "static", "fonts");
125
138
  await fs.promises.mkdir(fontsDir, { recursive: true });
126
139
  for (const fontFile of fontFiles) {
@@ -1 +1 @@
1
- {"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<FontRole, number[]> = {\n title: [400, 700],\n header: [400, 700],\n body: [400, 600],\n code: [400, 600],\n};\n\nexport const DEFAULT_ITALIC: Record<FontRole, boolean> = {\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<number>;\n italic: boolean;\n}\n\nfunction mergeInto(\n map: Map<string, MergedFontEntry>,\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<string, MergedFontEntry>();\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<string, string> = {\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<Partial<FontsOptions>> = (\n userOptions?: Partial<FontsOptions>,\n) => {\n const options: FontsOptions = { ...defaultOptions, ...userOptions };\n\n return {\n name: \"FontsEmitter\",\n async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator<FilePath> {\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"]}
1
+ {"version":3,"sources":["../src/defaults.ts","../src/util/google-fonts.ts","../src/util/process-fonts.ts","../src/util/typography.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<FontRole, number[]> = {\n title: [400, 700],\n header: [400, 700],\n body: [400, 600],\n code: [400, 600],\n};\n\nexport const DEFAULT_ITALIC: Record<FontRole, boolean> = {\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<number>;\n italic: boolean;\n}\n\nfunction mergeInto(\n map: Map<string, MergedFontEntry>,\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<string, MergedFontEntry>();\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<string, string> = {\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\n/**\n * Rewrites Google Fonts URLs to local files. Font files are written next to the\n * stylesheet (`static/fonts/`), so the rewritten URLs are relative to it and work\n * regardless of host or base path (jackyzha0/quartz#2547).\n */\nexport function processGoogleFonts(stylesheet: 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 = `${filename}.${extension}`;\n fontFiles.push({ url, filename, extension });\n return match.replace(url, rewrittenUrl);\n },\n );\n\n return { processedStylesheet, fontFiles };\n}\n","import type { BuildCtx } from \"@quartz-community/types\";\nimport type { FontSpecification } from \"../types\";\n\nexport const QUARTZ_DEFAULT_HEADER = \"Schibsted Grotesk\";\nexport const QUARTZ_DEFAULT_BODY = \"Source Sans Pro\";\nexport const QUARTZ_DEFAULT_CODE = \"IBM Plex Mono\";\n\nexport interface ConfiguredTypography {\n title?: FontSpecification;\n header?: FontSpecification;\n body?: FontSpecification;\n code?: FontSpecification;\n}\n\nfunction isFontSpecification(value: unknown): value is FontSpecification {\n if (typeof value === \"string\") return value.trim().length > 0;\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { name?: unknown }).name === \"string\"\n );\n}\n\n/**\n * Reads `configuration.theme.typography` from the Quartz build context.\n * Only well-formed font specifications are returned; anything else is omitted.\n */\nexport function readConfiguredTypography(ctx: BuildCtx | undefined): ConfiguredTypography {\n const theme = ctx?.cfg?.configuration?.theme as { typography?: unknown } | undefined;\n const typography = theme?.typography;\n if (typeof typography !== \"object\" || typography === null) return {};\n\n const result: ConfiguredTypography = {};\n for (const role of [\"title\", \"header\", \"body\", \"code\"] as const) {\n const spec = (typography as Record<string, unknown>)[role];\n if (isFontSpecification(spec)) result[role] = spec;\n }\n return result;\n}\n\n/**\n * Font specifications used to load fonts (Google Fonts / self-hosted):\n * plugin options first, then `configuration.theme.typography`, then the Quartz defaults.\n */\nexport function resolveFontSpecs(\n options: ConfiguredTypography,\n typography: ConfiguredTypography,\n): {\n title?: FontSpecification;\n header: FontSpecification;\n body: FontSpecification;\n code: FontSpecification;\n} {\n return {\n title: options.title ?? typography.title,\n header: options.header ?? typography.header ?? QUARTZ_DEFAULT_HEADER,\n body: options.body ?? typography.body ?? QUARTZ_DEFAULT_BODY,\n code: options.code ?? typography.code ?? QUARTZ_DEFAULT_CODE,\n };\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\";\nimport { readConfiguredTypography, resolveFontSpecs } from \"./util/typography\";\n\nconst defaultOptions: FontsOptions = {\n useThemeFonts: true,\n fontOrigin: \"googleFonts\",\n};\n\nexport const FontsEmitter: QuartzEmitterPlugin<Partial<FontsOptions>> = (\n userOptions?: Partial<FontsOptions>,\n) => {\n const options: FontsOptions = { ...defaultOptions, ...userOptions };\n\n return {\n name: \"FontsEmitter\",\n async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator<FilePath> {\n if (options.fontOrigin !== \"selfHosted\") {\n return;\n }\n\n const href = googleFontHref(resolveFontSpecs(options, readConfiguredTypography(ctx)));\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);\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"]}
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import path from 'path';
4
4
 
5
5
  const require$1 = createRequire(import.meta.url);
6
6
  var __require = /* @__PURE__ */ ((x2) => typeof require$1 !== "undefined" ? require$1 : typeof Proxy !== "undefined" ? new Proxy(x2, {
7
- get: (a2, b) => (typeof require$1 !== "undefined" ? require$1 : a2)[b]
7
+ get: (a2, b2) => (typeof require$1 !== "undefined" ? require$1 : a2)[b2]
8
8
  }) : x2)(function(x2) {
9
9
  if (typeof require$1 !== "undefined") return require$1.apply(this, arguments);
10
10
  throw Error('Dynamic require of "' + x2 + '" is not supported');
@@ -14,25 +14,25 @@ var __require = /* @__PURE__ */ ((x2) => typeof require$1 !== "undefined" ? requ
14
14
  var n;
15
15
  var l;
16
16
  var u;
17
- var v = [];
18
- function _(l2, u2, t2) {
19
- var i2, o2, r2, e2 = {};
20
- for (r2 in u2) "key" == r2 ? i2 = u2[r2] : "ref" == r2 ? o2 = u2[r2] : e2[r2] = u2[r2];
17
+ var w = [];
18
+ function k(l2, u3, t2) {
19
+ var i2, r2, o2, e2 = {};
20
+ for (o2 in u3) "key" == o2 ? i2 = u3[o2] : "ref" == o2 ? r2 = u3[o2] : e2[o2] = u3[o2];
21
21
  if (arguments.length > 2 && (e2.children = arguments.length > 3 ? n.call(arguments, 2) : t2), "function" == typeof l2) ;
22
- return m(l2, e2, i2, o2, null);
22
+ return x(l2, e2, i2, r2, null);
23
23
  }
24
- function m(n2, t2, i2, o2, r2) {
25
- var e2 = { type: n2, props: t2, key: i2, ref: o2, __k: null, __: null, __b: 0, __e: null, __c: null, constructor: void 0, __v: null == r2 ? ++u : r2, __i: -1, __u: 0 };
24
+ function x(n2, t2, i2, r2, o2) {
25
+ var e2 = { type: n2, props: t2, key: i2, ref: r2, __k: null, __: null, __b: 0, __e: null, __c: null, constructor: void 0, __v: null == o2 ? ++u : o2, __i: -1, __u: 0 };
26
26
  return null != l.vnode && l.vnode(e2), e2;
27
27
  }
28
- n = v.slice, l = { __e: function(n2, l2, u2, t2) {
29
- for (var i2, o2, r2; l2 = l2.__; ) if ((i2 = l2.__c) && !i2.__) try {
30
- if ((o2 = i2.constructor) && null != o2.getDerivedStateFromError && (i2.setState(o2.getDerivedStateFromError(n2)), r2 = i2.__d), null != i2.componentDidCatch && (i2.componentDidCatch(n2, t2 || {}), r2 = i2.__d), r2) return i2.__E = i2;
28
+ n = w.slice, l = { __e: function(n2, l2, u3, t2) {
29
+ for (var i2, r2, o2; l2 = l2.__; ) if ((i2 = l2.__c) && !i2.__) try {
30
+ if ((r2 = i2.constructor) && null != r2.getDerivedStateFromError && (i2.setState(r2.getDerivedStateFromError(n2)), o2 = i2.__d), null != i2.componentDidCatch && (i2.componentDidCatch(n2, t2 || {}), o2 = i2.__d), o2) return i2.__E = i2;
31
31
  } catch (l3) {
32
32
  n2 = l3;
33
33
  }
34
34
  throw n2;
35
- } }, u = 0, "function" == typeof Promise ? Promise.prototype.then.bind(Promise.resolve()) : setTimeout;
35
+ } }, u = 0, "function" == typeof Promise ? Promise.prototype.then.bind(Promise.resolve()) : setTimeout, Math.random().toString(8);
36
36
 
37
37
  // src/defaults.ts
38
38
  var OBSIDIAN_SANS_STACK = 'ui-sans-serif, -apple-system, BlinkMacSystemFont, system-ui, "Segoe UI", Roboto, "Inter", "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif';
@@ -80,7 +80,7 @@ function mergeInto(map, role, spec) {
80
80
  }
81
81
  }
82
82
  function formatMergedEntry(entry) {
83
- const sortedWeights = [...entry.weights].sort((a2, b) => a2 - b);
83
+ const sortedWeights = [...entry.weights].sort((a2, b2) => a2 - b2);
84
84
  const features = [];
85
85
  if (entry.italic) {
86
86
  features.push("ital");
@@ -160,10 +160,45 @@ function validateFontSpec(role, spec, fontRole) {
160
160
  return warnings;
161
161
  }
162
162
 
163
- // src/transformer.ts
163
+ // node_modules/@quartz-community/utils/dist/index.js
164
+ function pathToRoot(slug2) {
165
+ let rootPath = slug2.split("/").filter((x2) => x2 !== "").slice(0, -1).map((_2) => "..").join("/");
166
+ if (rootPath.length === 0) {
167
+ rootPath = ".";
168
+ }
169
+ return rootPath;
170
+ }
171
+
172
+ // src/util/typography.ts
164
173
  var QUARTZ_DEFAULT_HEADER = "Schibsted Grotesk";
165
174
  var QUARTZ_DEFAULT_BODY = "Source Sans Pro";
166
175
  var QUARTZ_DEFAULT_CODE = "IBM Plex Mono";
176
+ function isFontSpecification(value) {
177
+ if (typeof value === "string") return value.trim().length > 0;
178
+ return typeof value === "object" && value !== null && typeof value.name === "string";
179
+ }
180
+ function readConfiguredTypography(ctx) {
181
+ const theme = ctx?.cfg?.configuration?.theme;
182
+ const typography = theme?.typography;
183
+ if (typeof typography !== "object" || typography === null) return {};
184
+ const result = {};
185
+ for (const role of ["title", "header", "body", "code"]) {
186
+ const spec = typography[role];
187
+ if (isFontSpecification(spec)) result[role] = spec;
188
+ }
189
+ return result;
190
+ }
191
+ function resolveFontSpecs(options, typography) {
192
+ return {
193
+ title: options.title ?? typography.title,
194
+ header: options.header ?? typography.header ?? QUARTZ_DEFAULT_HEADER,
195
+ body: options.body ?? typography.body ?? QUARTZ_DEFAULT_BODY,
196
+ code: options.code ?? typography.code ?? QUARTZ_DEFAULT_CODE
197
+ };
198
+ }
199
+
200
+ // src/transformer.ts
201
+ var SELF_HOSTED_STYLESHEET = "static/fonts/quartz-fonts.css";
167
202
  var defaultOptions = {
168
203
  useThemeFonts: true,
169
204
  fontOrigin: "googleFonts"
@@ -172,7 +207,7 @@ function toFontFamily(spec) {
172
207
  if (typeof spec === "string") return spec;
173
208
  return spec.name;
174
209
  }
175
- function resolveFonts(options) {
210
+ function resolveFonts(options, typography) {
176
211
  const registry = options.useThemeFonts !== false ? readFontRegistry() : void 0;
177
212
  if (options.useThemeFonts !== false && !registry) {
178
213
  if (isQuartzThemeEnabled()) {
@@ -192,18 +227,21 @@ function resolveFonts(options) {
192
227
  const body = resolve(
193
228
  options.body,
194
229
  themeFonts["--font-text"],
230
+ typography.body && toFontFamily(typography.body),
195
231
  QUARTZ_DEFAULT_BODY,
196
232
  OBSIDIAN_SANS_STACK
197
233
  );
198
234
  const header = resolve(
199
235
  options.header,
200
236
  themeFonts["--font-text"],
237
+ typography.header && toFontFamily(typography.header),
201
238
  QUARTZ_DEFAULT_HEADER,
202
239
  OBSIDIAN_SANS_STACK
203
240
  );
204
241
  const code = resolve(
205
242
  options.code,
206
243
  themeFonts["--font-monospace"],
244
+ typography.code && toFontFamily(typography.code),
207
245
  QUARTZ_DEFAULT_CODE,
208
246
  OBSIDIAN_MONO_STACK
209
247
  );
@@ -212,7 +250,7 @@ function resolveFonts(options) {
212
250
  themeFonts["--font-interface"],
213
251
  OBSIDIAN_SANS_STACK
214
252
  );
215
- const title = resolve(options.title, void 0, header);
253
+ const title = resolve(options.title, typography.title && toFontFamily(typography.title), header);
216
254
  const resolveHeading = (level) => {
217
255
  const levelKey = `h${level}`;
218
256
  const themeVarKey = `--h${level}-font`;
@@ -286,20 +324,12 @@ function buildUnlayeredCSS(fonts) {
286
324
  `h6 { font-family: ${fonts.h6}; }`
287
325
  ].join("\n");
288
326
  }
289
- function buildGoogleFontsHead(options) {
290
- const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;
291
- const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;
292
- const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;
293
- const href = googleFontHref({
294
- title: options.title,
295
- header: headerSpec,
296
- body: bodySpec,
297
- code: codeSpec
298
- });
327
+ function buildGoogleFontsHead(options, typography) {
328
+ const href = googleFontHref(resolveFontSpecs(options, typography));
299
329
  return [
300
- _("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }),
301
- _("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }),
302
- _("link", { rel: "stylesheet", href })
330
+ k("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }),
331
+ k("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }),
332
+ k("link", { rel: "stylesheet", href })
303
333
  ];
304
334
  }
305
335
  var Fonts = (userOptions) => {
@@ -312,17 +342,23 @@ var Fonts = (userOptions) => {
312
342
  textTransform(_ctx, src) {
313
343
  return src;
314
344
  },
315
- externalResources(_ctx) {
316
- const fonts = resolveFonts(options);
345
+ externalResources(ctx) {
346
+ const typography = readConfiguredTypography(ctx);
347
+ const fonts = resolveFonts(options, typography);
317
348
  const css = [
318
349
  { content: buildLayeredCSS(fonts), inline: true },
319
350
  { content: buildUnlayeredCSS(fonts), inline: true }
320
351
  ];
321
352
  let additionalHead = [];
322
353
  if (options.fontOrigin === "googleFonts") {
323
- additionalHead = buildGoogleFontsHead(options);
354
+ additionalHead = buildGoogleFontsHead(options, typography);
324
355
  } else if (options.fontOrigin === "selfHosted") {
325
- additionalHead = [_("link", { rel: "stylesheet", href: "/static/fonts/quartz-fonts.css" })];
356
+ additionalHead = [
357
+ (fileData) => k("link", {
358
+ rel: "stylesheet",
359
+ href: `${pathToRoot(fileData?.slug ?? "index")}/${SELF_HOSTED_STYLESHEET}`
360
+ })
361
+ ];
326
362
  }
327
363
  return { css, js: [], additionalHead };
328
364
  }
@@ -337,13 +373,13 @@ var fontMimeMap = {
337
373
  opentype: "otf"
338
374
  };
339
375
  var fontUrlPattern = /url\((https:\/\/fonts.gstatic.com\/.+(?:\/|(?:kit=))(.+?)[.&].+?)\)\sformat\('(\w+?)'\);/g;
340
- function processGoogleFonts(stylesheet, baseUrl) {
376
+ function processGoogleFonts(stylesheet) {
341
377
  const fontFiles = [];
342
378
  const processedStylesheet = stylesheet.replace(
343
379
  fontUrlPattern,
344
380
  (match, url, filename, format) => {
345
381
  const extension = fontMimeMap[format] ?? format;
346
- const rewrittenUrl = `https://${baseUrl}/static/fonts/${filename}.${extension}`;
382
+ const rewrittenUrl = `${filename}.${extension}`;
347
383
  fontFiles.push({ url, filename, extension });
348
384
  return match.replace(url, rewrittenUrl);
349
385
  }
@@ -352,9 +388,6 @@ function processGoogleFonts(stylesheet, baseUrl) {
352
388
  }
353
389
 
354
390
  // src/emitter.ts
355
- var QUARTZ_DEFAULT_HEADER2 = "Schibsted Grotesk";
356
- var QUARTZ_DEFAULT_BODY2 = "Source Sans Pro";
357
- var QUARTZ_DEFAULT_CODE2 = "IBM Plex Mono";
358
391
  var defaultOptions2 = {
359
392
  useThemeFonts: true,
360
393
  fontOrigin: "googleFonts"
@@ -367,19 +400,7 @@ var FontsEmitter = (userOptions) => {
367
400
  if (options.fontOrigin !== "selfHosted") {
368
401
  return;
369
402
  }
370
- const baseUrl = ctx.cfg.configuration.baseUrl;
371
- if (!baseUrl) {
372
- throw new Error("[FontsEmitter] baseUrl is required for selfHosted fonts.");
373
- }
374
- const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER2;
375
- const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY2;
376
- const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE2;
377
- const href = googleFontHref({
378
- title: options.title,
379
- header: headerSpec,
380
- body: bodySpec,
381
- code: codeSpec
382
- });
403
+ const href = googleFontHref(resolveFontSpecs(options, readConfiguredTypography(ctx)));
383
404
  const cssResponse = await fetch(href);
384
405
  if (!cssResponse.ok) {
385
406
  throw new Error(
@@ -387,7 +408,7 @@ var FontsEmitter = (userOptions) => {
387
408
  );
388
409
  }
389
410
  const cssText = await cssResponse.text();
390
- const { processedStylesheet, fontFiles } = processGoogleFonts(cssText, baseUrl);
411
+ const { processedStylesheet, fontFiles } = processGoogleFonts(cssText);
391
412
  const fontsDir = path.join(ctx.argv.output, "static", "fonts");
392
413
  await fs.promises.mkdir(fontsDir, { recursive: true });
393
414
  for (const fontFile of fontFiles) {
package/dist/index.js.map CHANGED
@@ -1 +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<import('.').ComponentChildren>} [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<import('./internal').Component>}\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<import('./internal').Component> | 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<FontRole, number[]> = {\n title: [400, 700],\n header: [400, 700],\n body: [400, 600],\n code: [400, 600],\n};\n\nexport const DEFAULT_ITALIC: Record<FontRole, boolean> = {\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<string, unknown>)[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<string, unknown>);\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<number>;\n italic: boolean;\n}\n\nfunction mergeInto(\n map: Map<string, MergedFontEntry>,\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<string, MergedFontEntry>();\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<string, GoogleFontEntry>;\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<Partial<FontsOptions>> = (\n userOptions?: Partial<FontsOptions>,\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<string, string> = {\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<Partial<FontsOptions>> = (\n userOptions?: Partial<FontsOptions>,\n) => {\n const options: FontsOptions = { ...defaultOptions, ...userOptions };\n\n return {\n name: \"FontsEmitter\",\n async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator<FilePath> {\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"]}
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/diff/props.js","../node_modules/preact/src/create-context.js","../src/defaults.ts","../src/util/registry.ts","../src/util/google-fonts.ts","../src/util/validate.ts","../node_modules/@quartz-community/utils/src/path.ts","../src/util/typography.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","Math","random","toString","w","a","b","f","slug","x","_","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<import('.').ComponentChildren>} [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\tif (component._parentDom && component._dirty) {\n\t\tlet oldVNode = component._vnode,\n\t\t\toldDom = oldVNode._dom,\n\t\t\tcommitQueue = [],\n\t\t\trefQueue = [],\n\t\t\tnewVNode = 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\tvnode._children.some(child => {\n\t\t\tif (child != NULL && child._dom != NULL) {\n\t\t\t\treturn (vnode._dom = vnode._component.base = child._dom);\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<import('./internal').Component>}\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\ttry {\n\t\tlet c,\n\t\t\tl = 1;\n\n\t\t// Don't update `renderCount` yet. Keep its value non-zero to prevent unnecessary\n\t\t// process() calls from getting scheduled while `queue` is still being consumed.\n\t\twhile (rerenderQueue.length) {\n\t\t\t// Keep the rerender queue sorted by (depth, insertion order). The queue\n\t\t\t// will initially be sorted on the first iteration only if it has more than 1 item.\n\t\t\t//\n\t\t\t// New items can be added to the queue e.g. when rerendering a provider, so we want to\n\t\t\t// keep the order from top to bottom with those new items so we can handle them in a\n\t\t\t// single pass\n\t\t\tif (rerenderQueue.length > l) {\n\t\t\t\trerenderQueue.sort(depthSort);\n\t\t\t}\n\n\t\t\tc = rerenderQueue.shift();\n\t\t\tl = rerenderQueue.length;\n\n\t\t\trenderComponent(c);\n\t\t}\n\t} finally {\n\t\trerenderQueue.length = process._rerenderCount = 0;\n\t}\n}\n\nprocess._rerenderCount = 0;\n","import { IS_NON_DIMENSIONAL, NULL, SVG_NAMESPACE } from '../constants';\nimport options from '../options';\n\n// Per-instance unique key for event clock stamps. Each Preact copy on the page\n// gets its own random suffix so that `_dispatched` / `_attached` properties on\n// shared event objects and handler functions cannot collide across instances.\n// ~1 in 60M collision odds - if you have that many praect versions on the page,\n// you deserve some weird bugs.\n// In 11 we can replace this with a\n// Symbol\nlet _id = Math.random().toString(8),\n\tEVENT_DISPATCHED = '__d' + _id,\n\tEVENT_ATTACHED = '__a' + _id;\n\nfunction setStyle(style, key, value) {\n\tif (key[0] == '-') {\n\t\tstyle.setProperty(key, value == NULL ? '' : value);\n\t} else if (value == NULL) {\n\t\tstyle[key] = '';\n\t} else if (typeof value != 'number' || IS_NON_DIMENSIONAL.test(key)) {\n\t\tstyle[key] = value;\n\t} else {\n\t\tstyle[key] = value + 'px';\n\t}\n}\n\nconst CAPTURE_REGEX = /(PointerCapture)$|Capture$/i;\n\n// A logical clock to solve issues like https://github.com/preactjs/preact/issues/3927.\n// When the DOM performs an event it leaves micro-ticks in between bubbling up which means that\n// an event can trigger on a newly reated DOM-node while the event bubbles up.\n//\n// Originally inspired by Vue\n// (https://github.com/vuejs/core/blob/caeb8a68811a1b0f79/packages/runtime-dom/src/modules/events.ts#L90-L101),\n// but modified to use a logical clock instead of Date.now() in case event handlers get attached\n// and events get dispatched during the same millisecond.\n//\n// The clock is incremented after each new event dispatch. This allows 1 000 000 new events\n// per second for over 280 years before the value reaches Number.MAX_SAFE_INTEGER (2**53 - 1).\nlet eventClock = 0;\n\n/**\n * Set a property value on a DOM node\n * @param {import('../internal').PreactElement} dom The DOM node to modify\n * @param {string} name The name of the property to set\n * @param {*} value The value to set the property to\n * @param {*} oldValue The old value the property had\n * @param {string} namespace Whether or not this DOM node is an SVG node or not\n */\nexport function setProperty(dom, name, value, oldValue, namespace) {\n\tlet useCapture;\n\n\to: if (name == 'style') {\n\t\tif (typeof value == 'string') {\n\t\t\tdom.style.cssText = value;\n\t\t} else {\n\t\t\tif (typeof oldValue == 'string') {\n\t\t\t\tdom.style.cssText = oldValue = '';\n\t\t\t}\n\n\t\t\tif (oldValue) {\n\t\t\t\tfor (name in oldValue) {\n\t\t\t\t\tif (!(value && name in value)) {\n\t\t\t\t\t\tsetStyle(dom.style, name, '');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (value) {\n\t\t\t\tfor (name in value) {\n\t\t\t\t\tif (!oldValue || value[name] != oldValue[name]) {\n\t\t\t\t\t\tsetStyle(dom.style, name, value[name]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// Benchmark for comparison: https://esbench.com/bench/574c954bdb965b9a00965ac6\n\telse if (name[0] == 'o' && name[1] == 'n') {\n\t\tuseCapture = name != (name = name.replace(CAPTURE_REGEX, '$1'));\n\t\tconst lowerCaseName = name.toLowerCase();\n\n\t\t// Infer correct casing for DOM built-in events:\n\t\tif (lowerCaseName in dom || name == 'onFocusOut' || name == 'onFocusIn')\n\t\t\tname = lowerCaseName.slice(2);\n\t\telse name = name.slice(2);\n\n\t\tif (!dom._listeners) dom._listeners = {};\n\t\tdom._listeners[name + useCapture] = value;\n\n\t\tif (value) {\n\t\t\tif (!oldValue) {\n\t\t\t\tvalue[EVENT_ATTACHED] = eventClock;\n\t\t\t\tdom.addEventListener(\n\t\t\t\t\tname,\n\t\t\t\t\tuseCapture ? eventProxyCapture : eventProxy,\n\t\t\t\t\tuseCapture\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tvalue[EVENT_ATTACHED] = oldValue[EVENT_ATTACHED];\n\t\t\t}\n\t\t} else {\n\t\t\tdom.removeEventListener(\n\t\t\t\tname,\n\t\t\t\tuseCapture ? eventProxyCapture : eventProxy,\n\t\t\t\tuseCapture\n\t\t\t);\n\t\t}\n\t} else {\n\t\tif (namespace == SVG_NAMESPACE) {\n\t\t\t// Normalize incorrect prop usage for SVG:\n\t\t\t// - xlink:href / xlinkHref --> href (xlink:href was removed from SVG and isn't needed)\n\t\t\t// - className --> class\n\t\t\tname = name.replace(/xlink(H|:h)/, 'h').replace(/sName$/, 's');\n\t\t} else if (\n\t\t\tname != 'width' &&\n\t\t\tname != 'height' &&\n\t\t\tname != 'href' &&\n\t\t\tname != 'list' &&\n\t\t\tname != 'form' &&\n\t\t\t// Default value in browsers is `-1` and an empty string is\n\t\t\t// cast to `0` instead\n\t\t\tname != 'tabIndex' &&\n\t\t\tname != 'download' &&\n\t\t\tname != 'rowSpan' &&\n\t\t\tname != 'colSpan' &&\n\t\t\tname != 'role' &&\n\t\t\tname != 'popover' &&\n\t\t\tname in dom\n\t\t) {\n\t\t\ttry {\n\t\t\t\tdom[name] = value == NULL ? '' : value;\n\t\t\t\t// labelled break is 1b smaller here than a return statement (sorry)\n\t\t\t\tbreak o;\n\t\t\t} catch (e) {}\n\t\t}\n\n\t\t// aria- and data- attributes have no boolean representation.\n\t\t// A `false` value is different from the attribute not being\n\t\t// present, so we can't remove it. For non-boolean aria\n\t\t// attributes we could treat false as a removal, but the\n\t\t// amount of exceptions would cost too many bytes. On top of\n\t\t// that other frameworks generally stringify `false`.\n\n\t\tif (typeof value == 'function') {\n\t\t\t// never serialize functions as attribute values\n\t\t} else if (value != NULL && (value !== false || name[4] == '-')) {\n\t\t\tdom.setAttribute(name, name == 'popover' && value == true ? '' : value);\n\t\t} else {\n\t\t\tdom.removeAttribute(name);\n\t\t}\n\t}\n}\n\n/**\n * Create an event proxy function.\n * @param {boolean} useCapture Is the event handler for the capture phase.\n * @private\n */\nfunction createEventProxy(useCapture) {\n\t/**\n\t * Proxy an event to hooked event handlers\n\t * @param {import('../internal').PreactEvent} e The event object from the browser\n\t * @private\n\t */\n\treturn function (e) {\n\t\tif (this._listeners) {\n\t\t\tconst eventHandler = this._listeners[e.type + useCapture];\n\t\t\tif (e[EVENT_DISPATCHED] == NULL) {\n\t\t\t\te[EVENT_DISPATCHED] = eventClock++;\n\n\t\t\t\t// When `e[EVENT_DISPATCHED]` is smaller than the time when the targeted event\n\t\t\t\t// handler was attached we know we have bubbled up to an element that was added\n\t\t\t\t// during patching the DOM.\n\t\t\t} else if (e[EVENT_DISPATCHED] < eventHandler[EVENT_ATTACHED]) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treturn eventHandler(options.event ? options.event(e) : e);\n\t\t}\n\t};\n}\n\nconst eventProxy = createEventProxy(false);\nconst eventProxyCapture = createEventProxy(true);\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<import('./internal').Component> | 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<FontRole, number[]> = {\n title: [400, 700],\n header: [400, 700],\n body: [400, 600],\n code: [400, 600],\n};\n\nexport const DEFAULT_ITALIC: Record<FontRole, boolean> = {\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<string, unknown>)[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<string, unknown>);\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<number>;\n italic: boolean;\n}\n\nfunction mergeInto(\n map: Map<string, MergedFontEntry>,\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<string, MergedFontEntry>();\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<string, GoogleFontEntry>;\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 { slug as slugAnchor } from \"github-slugger\";\nimport type { Element as HastElement } from \"hast\";\nimport type { FilePath, FullSlug } from \"@quartz-community/types\";\n\nexport type { FilePath, FullSlug };\n\n/** No '/index' ending, no file extension, can have trailing slash for folders. */\nexport type SimpleSlug = string & { _brand: \"SimpleSlug\" };\n\n/** Starts with './' or '../', used for navigation. */\nexport type RelativeURL = string & { _brand: \"RelativeURL\" };\n\nexport interface TransformOptions {\n strategy: \"absolute\" | \"relative\" | \"shortest\";\n allSlugs: FullSlug[];\n}\n\nexport function isFilePath(s: string): s is FilePath {\n const validStart = !s.startsWith(\".\");\n return validStart && _hasFileExtension(s);\n}\n\nexport function isFullSlug(s: string): s is FullSlug {\n const validStart = !(s.startsWith(\".\") || s.startsWith(\"/\"));\n const validEnding = !s.endsWith(\"/\");\n return validStart && validEnding && !_containsForbiddenCharacters(s);\n}\n\nexport function isSimpleSlug(s: string): s is SimpleSlug {\n const validStart = !(s.startsWith(\".\") || (s.length > 1 && s.startsWith(\"/\")));\n const validEnding = !endsWith(s, \"index\");\n return validStart && !_containsForbiddenCharacters(s) && validEnding && !_hasFileExtension(s);\n}\n\nexport function isRelativeURL(s: string): s is RelativeURL {\n const validStart = /^\\.{1,2}/.test(s);\n const validEnding = !endsWith(s, \"index\");\n return validStart && validEnding && ![\".md\", \".html\"].includes(getFileExtension(s) ?? \"\");\n}\n\nexport function isAbsoluteURL(s: string): boolean {\n try {\n new URL(s);\n } catch {\n return false;\n }\n return true;\n}\n\nexport function simplifySlug(fp: FullSlug | string): SimpleSlug {\n const res = stripSlashes(trimSuffix(fp, \"index\"), true);\n return (res.length === 0 ? \"/\" : res) as SimpleSlug;\n}\n\nexport function getFullSlug(window: Window): FullSlug {\n const res = window.document.body.dataset.slug! as FullSlug;\n return res;\n}\n\nexport function getFullSlugFromUrl(): FullSlug {\n let rawSlug = decodeURI(window.location.pathname);\n if (rawSlug.endsWith(\"/\")) rawSlug = rawSlug.slice(0, -1);\n if (rawSlug.startsWith(\"/\")) rawSlug = rawSlug.slice(1);\n return rawSlug as FullSlug;\n}\n\nexport function slugifyFilePath(fp: FilePath, excludeExt?: boolean): FullSlug {\n fp = stripSlashes(fp) as FilePath;\n const ext = getFileExtension(fp);\n const withoutFileExt = fp.replace(new RegExp(ext + \"$\"), \"\");\n const finalExt = excludeExt || [\".md\", \".html\", undefined].includes(ext) ? \"\" : ext;\n\n let slug = _sluggify(withoutFileExt);\n\n if (endsWith(slug, \"_index\")) {\n slug = slug.replace(/_index$/, \"index\");\n }\n\n // Obsidian \"Folder Notes\" convention (insideFolder storage): a file named the\n // same as its parent folder is treated as the folder's landing page. Rewrite\n // `folder/folder` → `folder/index` so every downstream consumer (trie, link\n // resolver, simplifySlug, isFolderPath) sees the canonical Quartz slug shape\n // and Just Works. A top-level single-segment slug is NOT rewritten — that\n // represents Obsidian's `parentFolder` storage mode, which Quartz already\n // routes as a regular top-level page.\n const segments = slug.split(\"/\");\n if (segments.length >= 2 && segments[segments.length - 1] === segments[segments.length - 2]) {\n segments[segments.length - 1] = \"index\";\n slug = segments.join(\"/\");\n }\n\n return (slug + (finalExt ?? \"\")) as FullSlug;\n}\n\nexport function joinSegments(...args: string[]): string {\n if (args.length === 0) {\n return \"\";\n }\n\n let joined = args\n .filter((segment) => segment !== \"\" && segment !== \"/\")\n .map((segment) => stripSlashes(segment))\n .join(\"/\");\n\n const first = args[0];\n const last = args[args.length - 1];\n\n if (first?.startsWith(\"/\")) {\n joined = \"/\" + joined;\n }\n\n if (last?.endsWith(\"/\")) {\n joined = joined + \"/\";\n }\n\n return joined;\n}\n\nexport function resolvePath(to: string): string {\n if (to.startsWith(\"/\")) return to;\n return \"/\" + to;\n}\n\n/** Read the base path injected at build time via `data-basepath` on `<body>`.\n * Returns `\"\"` for root deployments, e.g. `\"/repository\"` for subdirectory. */\nexport function getBasePath(): string {\n if (typeof document === \"undefined\") return \"\";\n return document.body?.dataset?.basepath ?? \"\";\n}\n\n/** Resolve a slug to an absolute URL path, prepending the site's base path.\n * e.g. `resolveBasePath(\"features/Callouts\")` → `\"/repository/features/Callouts\"` */\nexport function resolveBasePath(to: string, basePath?: string): string {\n const base = basePath ?? getBasePath();\n const slug = to.startsWith(\"/\") ? to : \"/\" + to;\n return base + slug;\n}\n\nexport function endsWith(s: string, suffix: string): boolean {\n return s === suffix || s.endsWith(\"/\" + suffix);\n}\n\nexport function trimSuffix(s: string, suffix: string): string {\n if (endsWith(s, suffix)) {\n s = s.slice(0, -suffix.length);\n }\n return s;\n}\n\nexport function stripSlashes(s: string, onlyStripPrefix?: boolean): string {\n if (s.startsWith(\"/\")) {\n s = s.substring(1);\n }\n\n if (!onlyStripPrefix && s.endsWith(\"/\")) {\n s = s.slice(0, -1);\n }\n\n return s;\n}\n\nexport function getFileExtension(s: string): string | undefined {\n return s.match(/\\.[A-Za-z0-9]+$/)?.[0];\n}\n\nexport function isFolderPath(fplike: string): boolean {\n return (\n fplike.endsWith(\"/\") ||\n endsWith(fplike, \"index\") ||\n endsWith(fplike, \"index.md\") ||\n endsWith(fplike, \"index.html\")\n );\n}\n\nexport function getAllSegmentPrefixes(path: string): string[] {\n const segments = path.split(\"/\");\n const results: string[] = [];\n for (let i = 0; i < segments.length; i++) {\n results.push(segments.slice(0, i + 1).join(\"/\"));\n }\n return results;\n}\n\nexport function pathToRoot(slug: FullSlug): RelativeURL {\n let rootPath = slug\n .split(\"/\")\n .filter((x) => x !== \"\")\n .slice(0, -1)\n .map((_) => \"..\")\n .join(\"/\");\n\n if (rootPath.length === 0) {\n rootPath = \".\";\n }\n\n return rootPath as RelativeURL;\n}\n\nexport function resolveRelative(current: FullSlug, target: FullSlug | SimpleSlug): RelativeURL {\n const res = joinSegments(pathToRoot(current), simplifySlug(target as FullSlug)) as RelativeURL;\n return res;\n}\n\nexport function splitAnchor(link: string): [string, string] {\n const [fp, anchor] = link.split(\"#\", 2);\n if (fp!.endsWith(\".pdf\")) {\n return [fp!, anchor === undefined ? \"\" : `#${anchor}`];\n }\n if (anchor === undefined) {\n return [fp!, \"\"];\n }\n const bare = anchor.startsWith(\"^\") ? anchor.slice(1) : anchor;\n const slugged = \"#\" + slugAnchor(bare);\n return [fp!, slugged];\n}\n\nexport function slugTag(tag: string): string {\n return tag\n .split(\"/\")\n .map((tagSegment) => _sluggify(tagSegment))\n .join(\"/\");\n}\n\nexport function transformInternalLink(link: string): RelativeURL {\n const [fplike, anchor] = splitAnchor(decodeURI(link));\n\n const segments = fplike.split(\"/\").filter((x) => x.length > 0);\n const prefix = segments.filter(_isRelativeSegment).join(\"/\");\n const fp = segments.filter((seg) => !_isRelativeSegment(seg) && seg !== \"\").join(\"/\");\n\n const slugged = slugifyFilePath(fp as FilePath);\n const simpleSlug = simplifySlug(slugged);\n const folderPath = isFolderPath(fplike) || isFolderPath(slugged);\n const joined = joinSegments(stripSlashes(prefix), stripSlashes(simpleSlug));\n const trail = folderPath ? \"/\" : \"\";\n const res = (_addRelativeToStart(joined) + trail + anchor) as RelativeURL;\n return res;\n}\n\nexport function transformLink(src: FullSlug, target: string, opts: TransformOptions): RelativeURL {\n const targetSlug = transformInternalLink(target);\n\n if (opts.strategy === \"relative\") {\n return targetSlug as RelativeURL;\n } else {\n // When src is a folder page (e.g. \"Compendium/Spells\" backed by index.md),\n // the browser serves it at \"/Compendium/Spells/\" with a trailing slash.\n // pathToRoot needs the \"/index\" suffix to compute the correct depth.\n const effectiveSrc =\n !endsWith(src, \"index\") && opts.allSlugs.includes(`${src}/index` as FullSlug)\n ? (`${src}/index` as FullSlug)\n : src;\n\n const folderTail = isFolderPath(targetSlug) ? \"/\" : \"\";\n const canonicalSlug = stripSlashes(targetSlug.slice(\".\".length));\n const [targetCanonical, targetAnchor] = splitAnchor(canonicalSlug);\n\n if (opts.strategy === \"shortest\") {\n const isMultiSegment = targetCanonical.includes(\"/\");\n const isFolderTarget = isFolderPath(targetSlug);\n const matchingFileNames = opts.allSlugs.filter((slug) => {\n if (isMultiSegment) {\n // Multi-segment partial path: match by suffix\n if (slug === targetCanonical || slug.endsWith(\"/\" + targetCanonical)) {\n return true;\n }\n // Folder targets have \"/index\" stripped — also match the \"/index\" variant\n if (isFolderTarget) {\n const withIndex = targetCanonical + \"/index\";\n return slug === withIndex || slug.endsWith(\"/\" + withIndex);\n }\n return false;\n }\n const parts = slug.split(\"/\");\n const fileName = parts.at(-1);\n return targetCanonical === fileName;\n });\n\n if (matchingFileNames.length === 1) {\n const matchedSlug = matchingFileNames[0]!;\n return (resolveRelative(effectiveSrc, matchedSlug) + targetAnchor) as RelativeURL;\n }\n }\n\n return (joinSegments(pathToRoot(effectiveSrc), canonicalSlug) + folderTail) as RelativeURL;\n }\n}\n\n/**\n * Slugify a file path for use as an href.\n * Replaces whitespace→hyphens, &→-and-, %→-percent, removes ? and #, and\n * lowercases the result so that link matching is case-insensitive (matching\n * Obsidian's link-resolution semantics). Operates per segment so directory\n * separators are preserved.\n */\nexport function slugifyPath(s: string): string {\n return s\n .split(\"/\")\n .map((segment) =>\n segment\n .replace(/\\s/g, \"-\")\n .replace(/&/g, \"-and-\")\n .replace(/%/g, \"-percent\")\n .replace(/\\?/g, \"\")\n .replace(/#/g, \"\")\n .replace(/[<>:\"|*]/g, \"\")\n .toLowerCase(),\n )\n .join(\"/\")\n .replace(/\\/$/, \"\");\n}\n\n/**\n * Re-base the relative `href` and `src` attributes of a HAST element so that\n * links inside content originally resolved relative to `newBase` remain valid\n * when the element is embedded at a page with slug `curBase`.\n *\n * Used for transclusions and cross-slug HAST embedding (Quartz core's\n * `renderPage` transclude expansion, canvas-page's embedded file-nodes, and\n * any plugin that takes `vfile.data.htmlAst` from one page and renders it\n * inside another). Absolute URLs pass through unchanged.\n *\n * The element (and its children) are deep-cloned via `structuredClone`, so\n * the original HAST tree is never mutated.\n */\nexport function normalizeHastElement<T extends HastElement>(\n rawEl: T,\n curBase: FullSlug,\n newBase: FullSlug,\n): T {\n const el = structuredClone(rawEl);\n _rebaseHastElement(el, \"src\", curBase, newBase);\n _rebaseHastElement(el, \"href\", curBase, newBase);\n if (el.children) {\n el.children = el.children.map((child) =>\n child.type === \"element\"\n ? (normalizeHastElement(child as HastElement, curBase, newBase) as typeof child)\n : child,\n );\n }\n return el;\n}\n\n/** @internal */\nfunction _rebaseHastElement(\n el: HastElement,\n attr: \"href\" | \"src\",\n curBase: FullSlug,\n newBase: FullSlug,\n): void {\n const value = el.properties?.[attr];\n if (value === undefined || value === null) return;\n const href = String(value);\n if (!isRelativeURL(href)) return;\n el.properties![attr] = joinSegments(resolveRelative(curBase, newBase), \"..\", href);\n}\n\n/** @internal */\nfunction _sluggify(s: string): string {\n return slugifyPath(s);\n}\n\nfunction _containsForbiddenCharacters(s: string): boolean {\n return s.includes(\" \") || s.includes(\"#\") || s.includes(\"?\") || s.includes(\"&\");\n}\n\nfunction _hasFileExtension(s: string): boolean {\n return getFileExtension(s) !== undefined;\n}\n\nfunction _isRelativeSegment(s: string): boolean {\n return /^\\.{0,2}$/.test(s);\n}\n\nfunction _addRelativeToStart(s: string): string {\n if (s === \"\") {\n s = \".\";\n }\n\n if (!s.startsWith(\".\")) {\n s = joinSegments(\".\", s);\n }\n\n return s;\n}\n","import type { BuildCtx } from \"@quartz-community/types\";\nimport type { FontSpecification } from \"../types\";\n\nexport const QUARTZ_DEFAULT_HEADER = \"Schibsted Grotesk\";\nexport const QUARTZ_DEFAULT_BODY = \"Source Sans Pro\";\nexport const QUARTZ_DEFAULT_CODE = \"IBM Plex Mono\";\n\nexport interface ConfiguredTypography {\n title?: FontSpecification;\n header?: FontSpecification;\n body?: FontSpecification;\n code?: FontSpecification;\n}\n\nfunction isFontSpecification(value: unknown): value is FontSpecification {\n if (typeof value === \"string\") return value.trim().length > 0;\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { name?: unknown }).name === \"string\"\n );\n}\n\n/**\n * Reads `configuration.theme.typography` from the Quartz build context.\n * Only well-formed font specifications are returned; anything else is omitted.\n */\nexport function readConfiguredTypography(ctx: BuildCtx | undefined): ConfiguredTypography {\n const theme = ctx?.cfg?.configuration?.theme as { typography?: unknown } | undefined;\n const typography = theme?.typography;\n if (typeof typography !== \"object\" || typography === null) return {};\n\n const result: ConfiguredTypography = {};\n for (const role of [\"title\", \"header\", \"body\", \"code\"] as const) {\n const spec = (typography as Record<string, unknown>)[role];\n if (isFontSpecification(spec)) result[role] = spec;\n }\n return result;\n}\n\n/**\n * Font specifications used to load fonts (Google Fonts / self-hosted):\n * plugin options first, then `configuration.theme.typography`, then the Quartz defaults.\n */\nexport function resolveFontSpecs(\n options: ConfiguredTypography,\n typography: ConfiguredTypography,\n): {\n title?: FontSpecification;\n header: FontSpecification;\n body: FontSpecification;\n code: FontSpecification;\n} {\n return {\n title: options.title ?? typography.title,\n header: options.header ?? typography.header ?? QUARTZ_DEFAULT_HEADER,\n body: options.body ?? typography.body ?? QUARTZ_DEFAULT_BODY,\n code: options.code ?? typography.code ?? QUARTZ_DEFAULT_CODE,\n };\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\";\nimport { pathToRoot } from \"@quartz-community/utils\";\nimport type { FullSlug } from \"@quartz-community/types\";\nimport {\n QUARTZ_DEFAULT_BODY,\n QUARTZ_DEFAULT_CODE,\n QUARTZ_DEFAULT_HEADER,\n readConfiguredTypography,\n resolveFontSpecs,\n} from \"./util/typography\";\nimport type { ConfiguredTypography } from \"./util/typography\";\n\nexport const SELF_HOSTED_STYLESHEET = \"static/fonts/quartz-fonts.css\";\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, typography: ConfiguredTypography): 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 typography.body && toFontFamily(typography.body),\n QUARTZ_DEFAULT_BODY,\n OBSIDIAN_SANS_STACK,\n );\n const header = resolve(\n options.header,\n themeFonts[\"--font-text\"],\n typography.header && toFontFamily(typography.header),\n QUARTZ_DEFAULT_HEADER,\n OBSIDIAN_SANS_STACK,\n );\n const code = resolve(\n options.code,\n themeFonts[\"--font-monospace\"],\n typography.code && toFontFamily(typography.code),\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, typography.title && toFontFamily(typography.title), 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, typography: ConfiguredTypography): unknown[] {\n const href = googleFontHref(resolveFontSpecs(options, typography));\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<Partial<FontsOptions>> = (\n userOptions?: Partial<FontsOptions>,\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 typography = readConfiguredTypography(ctx);\n const fonts = resolveFonts(options, typography);\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, typography);\n } else if (options.fontOrigin === \"selfHosted\") {\n // Relative to the page so it works on localhost and under a base path (jackyzha0/quartz#2547).\n additionalHead = [\n (fileData: { slug?: string }) =>\n h(\"link\", {\n rel: \"stylesheet\",\n href: `${pathToRoot((fileData?.slug ?? \"index\") as FullSlug)}/${SELF_HOSTED_STYLESHEET}`,\n }),\n ];\n }\n\n return { css, js: [], additionalHead };\n },\n };\n};\n","import type { ProcessedFontResult, GoogleFontFile } from \"../types\";\n\nconst fontMimeMap: Record<string, string> = {\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\n/**\n * Rewrites Google Fonts URLs to local files. Font files are written next to the\n * stylesheet (`static/fonts/`), so the rewritten URLs are relative to it and work\n * regardless of host or base path (jackyzha0/quartz#2547).\n */\nexport function processGoogleFonts(stylesheet: 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 = `${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\";\nimport { readConfiguredTypography, resolveFontSpecs } from \"./util/typography\";\n\nconst defaultOptions: FontsOptions = {\n useThemeFonts: true,\n fontOrigin: \"googleFonts\",\n};\n\nexport const FontsEmitter: QuartzEmitterPlugin<Partial<FontsOptions>> = (\n userOptions?: Partial<FontsOptions>,\n) => {\n const options: FontsOptions = { ...defaultOptions, ...userOptions };\n\n return {\n name: \"FontsEmitter\",\n async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator<FilePath> {\n if (options.fontOrigin !== \"selfHosted\") {\n return;\n }\n\n const href = googleFontHref(resolveFontSpecs(options, readConfiguredTypography(ctx)));\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);\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quartz-community/quartz-fonts",
3
- "version": "0.1.1",
3
+ "version": "1.0.1",
4
4
  "description": "Fine-grained font control for Quartz sites. Supports per-heading fonts, theme font discovery, and Obsidian-compatible defaults.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -45,7 +45,9 @@
45
45
  "test": "vitest run",
46
46
  "typecheck": "tsc --noEmit",
47
47
  "check": "npm run typecheck && npm run lint && npm run format && npm run test",
48
- "prepublishOnly": "npm run build"
48
+ "prepublishOnly": "npm run build",
49
+ "release": "npm run build && changeset publish",
50
+ "version-packages": "changeset version && npm install --package-lock-only --ignore-scripts --no-audit --no-fund"
49
51
  },
50
52
  "peerDependencies": {
51
53
  "google-font-metadata": "^6.0.0",
@@ -64,8 +66,8 @@
64
66
  }
65
67
  },
66
68
  "dependencies": {
67
- "@quartz-community/types": "^0.2.1",
68
- "@quartz-community/utils": "^0.1.0"
69
+ "@quartz-community/types": "^1.0.0",
70
+ "@quartz-community/utils": "^1.0.0"
69
71
  },
70
72
  "devDependencies": {
71
73
  "@types/hast": "^3.0.4",
@@ -84,7 +86,8 @@
84
86
  "typescript": "^5.9.3",
85
87
  "unified": "^11.0.5",
86
88
  "vfile": "^6.0.3",
87
- "vitest": "^2.1.9"
89
+ "vitest": "^2.1.9",
90
+ "@changesets/cli": "^2.29.2"
88
91
  },
89
92
  "quartz": {
90
93
  "name": "fonts",