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