From e86544064cf37e7cdb7cac302cfb40fdb728de6d Mon Sep 17 00:00:00 2001
From: Jacky Zhao <j.zhao2k19@gmail.com>
Date: Sun, 16 Mar 2025 22:12:40 +0000
Subject: [PATCH] fix: parse parallelization chunk arg, inline b64 for og image
---
quartz/util/og.tsx | 54 ++++++++++++---------------
quartz/util/log.ts | 2
quartz/processors/parse.ts | 4 +-
quartz/plugins/emitters/ogImage.tsx | 24 +++++++++++-
4 files changed, 49 insertions(+), 35 deletions(-)
diff --git a/quartz/plugins/emitters/ogImage.tsx b/quartz/plugins/emitters/ogImage.tsx
index f31cc4b..0b78695 100644
--- a/quartz/plugins/emitters/ogImage.tsx
+++ b/quartz/plugins/emitters/ogImage.tsx
@@ -1,7 +1,7 @@
import { QuartzEmitterPlugin } from "../types"
import { i18n } from "../../i18n"
import { unescapeHTML } from "../../util/escape"
-import { FullSlug, getFileExtension } from "../../util/path"
+import { FullSlug, getFileExtension, joinSegments, QUARTZ } from "../../util/path"
import { ImageOptions, SocialImageOptions, defaultImage, getSatoriFonts } from "../../util/og"
import sharp from "sharp"
import satori, { SatoriOptions } from "satori"
@@ -10,6 +10,8 @@
import { write } from "./helpers"
import { BuildCtx } from "../../util/ctx"
import { QuartzPluginData } from "../vfile"
+import fs from "node:fs/promises"
+import chalk from "chalk"
const defaultOptions: SocialImageOptions = {
colorScheme: "lightMode",
@@ -28,7 +30,25 @@
userOpts: SocialImageOptions,
): Promise<Readable> {
const { width, height } = userOpts
- const imageComponent = userOpts.imageStructure(cfg, userOpts, title, description, fonts, fileData)
+ const iconPath = joinSegments(QUARTZ, "static", "icon.png")
+ let iconBase64: string | undefined = undefined
+ try {
+ const iconData = await fs.readFile(iconPath)
+ iconBase64 = `data:image/png;base64,${iconData.toString("base64")}`
+ } catch (err) {
+ console.warn(chalk.yellow(`Warning: Could not find icon at ${iconPath}`))
+ }
+
+ const imageComponent = userOpts.imageStructure({
+ cfg,
+ userOpts,
+ title,
+ description,
+ fonts,
+ fileData,
+ iconBase64,
+ })
+
const svg = await satori(imageComponent, {
width,
height,
diff --git a/quartz/processors/parse.ts b/quartz/processors/parse.ts
index 3a0d15a..04efdbe 100644
--- a/quartz/processors/parse.ts
+++ b/quartz/processors/parse.ts
@@ -172,7 +172,7 @@
workerType: "thread",
})
const errorHandler = (err: any) => {
- console.error(`${err}`.replace(/^error:\s*/i, ""))
+ console.error(err)
process.exit(1)
}
@@ -201,7 +201,7 @@
const markdownToHtmlPromises: WorkerPromise<ProcessedContent[]>[] = []
processedFiles = 0
- for (const [mdChunk, _] of mdResults) {
+ for (const mdChunk of mdResults) {
markdownToHtmlPromises.push(pool.exec("processHtml", [serializableCtx, mdChunk]))
}
const results: ProcessedContent[][] = await Promise.all(
diff --git a/quartz/util/log.ts b/quartz/util/log.ts
index 2d53dd3..cfd8c3f 100644
--- a/quartz/util/log.ts
+++ b/quartz/util/log.ts
@@ -35,7 +35,7 @@
const truncated = truncate(output, columns)
process.stdout.write(truncated)
this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerChars.length
- }, 20)
+ }, 50)
}
}
diff --git a/quartz/util/og.tsx b/quartz/util/og.tsx
index 4901a53..41f885b 100644
--- a/quartz/util/og.tsx
+++ b/quartz/util/og.tsx
@@ -13,6 +13,7 @@
const defaultHeaderWeight = [700]
const defaultBodyWeight = [400]
+
export async function getSatoriFonts(headerFont: FontSpecification, bodyFont: FontSpecification) {
// Get all weights for header and body fonts
const headerWeights: FontWeight[] = (
@@ -134,21 +135,12 @@
excludeRoot: boolean
/**
* JSX to use for generating image. See satori docs for more info (https://github.com/vercel/satori)
- * @param cfg global quartz config
- * @param userOpts options that can be set by user
- * @param title title of current page
- * @param description description of current page
- * @param fonts global font that can be used for styling
- * @param fileData full fileData of current page
- * @returns prepared jsx to be used for generating image
*/
imageStructure: (
- cfg: GlobalConfiguration,
- userOpts: UserOpts,
- title: string,
- description: string,
- fonts: SatoriOptions["fonts"],
- fileData: QuartzPluginData,
+ options: ImageOptions & {
+ userOpts: UserOpts
+ iconBase64?: string
+ },
) => JSXInternal.Element
}
@@ -178,17 +170,17 @@
}
// This is the default template for generated social image.
-export const defaultImage: SocialImageOptions["imageStructure"] = (
- cfg: GlobalConfiguration,
- { colorScheme }: UserOpts,
- title: string,
- description: string,
- _fonts: SatoriOptions["fonts"],
- fileData: QuartzPluginData,
-) => {
+export const defaultImage: SocialImageOptions["imageStructure"] = ({
+ cfg,
+ userOpts,
+ title,
+ description,
+ fileData,
+ iconBase64,
+}) => {
+ const { colorScheme } = userOpts
const fontBreakPoint = 32
const useSmallerFont = title.length > fontBreakPoint
- const iconPath = `https://${cfg.baseUrl}/static/icon.png`
// Format date if available
const rawDate = getDate(cfg, fileData)
@@ -226,14 +218,16 @@
marginBottom: "0.5rem",
}}
>
- <img
- src={iconPath}
- width={56}
- height={56}
- style={{
- borderRadius: "50%",
- }}
- />
+ {iconBase64 && (
+ <img
+ src={iconBase64}
+ width={56}
+ height={56}
+ style={{
+ borderRadius: "50%",
+ }}
+ />
+ )}
<div
style={{
display: "flex",
--
Gitblit v1.10.0