feat(open-graph): generate OG images + further OG support (#740)
* Quartz sync: Aug 29, 2023, 10:17 PM * feat: add basic satori og image generation * Squashed commit of the following: commitfa69c2a565Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com> Date: Thu Sep 21 19:35:11 2023 +0200 fix(explorer): increase consistency, explicitly use font-family (#496) * fix(explorer): display name for folders without `index` file * docs(explorer): add section for folder display names * docs(explorer): fix broken wikilink * fix(consistency): explicitly set font + label/link fix Use consistent styling between folders with `folderClickBehavior: "link"` and `"collapse` * Update quartz/components/styles/explorer.scss Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * Update quartz/components/styles/explorer.scss Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> --------- Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> commit8eb1554b13Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com> Date: Thu Sep 21 18:54:33 2023 +0200 fix(explorer): display names for folders without frontmatter (#494) * fix(explorer): display name for folders without `index` file * docs(explorer): add section for folder display names commitdcdeae4e7bAuthor: Ben Schlegel <31989404+benschlegel@users.noreply.github.com> Date: Thu Sep 21 18:53:19 2023 +0200 docs(explorer): update default config + new example (#493) commit48452231d5Author: Jacky Zhao <j.zhao2k19@gmail.com> Date: Wed Sep 20 16:09:18 2023 -0700 perf: memoize filetree computation (#490) * perf: memoize filetree computation * format * var -> let commit16d33fb771Author: Jacky Zhao <j.zhao2k19@gmail.com> Date: Wed Sep 20 16:08:54 2023 -0700 feat: display name for folders, expand explorer a little bit (#489) * feat: display name for folders, expand explorer a little bit * update docs commitb029eeadabAuthor: Ben Schlegel <31989404+benschlegel@users.noreply.github.com> Date: Wed Sep 20 22:55:29 2023 +0200 feat(explorer): improve accessibility and consistency (+ bug fix) (#488) * feat(consistency): use `all: unset` on button * style: improve accessibility and consistency for explorer * fix: localStorage bug with folder name changes * chore: bump quartz version commit6a9e6352e8Author: Jacky Zhao <j.zhao2k19@gmail.com> Date: Wed Sep 20 13:52:45 2023 -0700 Revert "feat: Making Quartz available offline by making it a PWA (#465)" This reverts commitd6301fae90. commit70e029d151Author: Jacky Zhao <j.zhao2k19@gmail.com> Date: Wed Sep 20 13:52:29 2023 -0700 Revert "docs: wording changes for offline support" This reverts commit52a172d1a4. commit0bad3ce799Author: Jacky Zhao <j.zhao2k19@gmail.com> Date: Wed Sep 20 11:58:52 2023 -0700 docs: document enableToc commit52a172d1a4Author: Jacky Zhao <j.zhao2k19@gmail.com> Date: Wed Sep 20 11:40:36 2023 -0700 docs: wording changes for offline support commitd6301fae90Author: Adam Brangenberg <adambrangenberg@proton.me> Date: Wed Sep 20 20:38:13 2023 +0200 feat: Making Quartz available offline by making it a PWA (#465) * Adding PWA and chaching for offline aviability * renamed workbox config to fit Quartz' scheme * Documenting new configuration * Added missig umami documentation * Fixed formatting so the build passes, thank you prettier :) * specified caching strategies to improve performance * formatting... * fixing "404 manifest.json not found" on subdirectories by adding a / to manifestpath * turning it into a plugin * Removed Workbox-cli and updated @types/node * Added Serviceworkercode to offline.ts * formatting * Removing workbox from docs * applied suggestions * Removed path.join for sw path Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * Removed path.join for manifest path Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * Removing path module import * Added absolute path to manifests start_url and manifest "import" using baseUrl * Adding protocol to baseurl Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * Adding protocol to start_url too then * formatting... * Adding fallback page * Documenting offline plugin * formatting... * merge suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * merge suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * merge suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * merge suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * merge suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * merge suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * merge suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * merge suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * merge suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * merge suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * merge suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * merge suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * formatting... * Fixing manifest path, all these nits hiding the actual issues .-. * Offline fallback page through plugins, most things taken from 404 Plugin * adding Offline Plugin to config * formatting... * Turned offline off as default and removed offline.md --------- Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> commit27a6087dd5Author: rwutscher <richard.wutscher@gmail.com> Date: Tue Sep 19 21:26:30 2023 +0200 fix: tag regex no longer includes purely numerical 'tags' (#485) * fix: tag regex no longer includes purely numerical 'tags' * fix: formatting * fix: use guard in findAndReplace() instead of expanding the regex commit1bf7e3d8b3Author: Jacky Zhao <j.zhao2k19@gmail.com> Date: Tue Sep 19 10:22:39 2023 -0700 fix(nit): make defaultOptions on explorer not a function commitcc31a40b0cAuthor: David Fischer <david@konst.fish> Date: Tue Sep 19 18:25:51 2023 +0200 feat: support changes in system theme (#484) * feat: support changes in system theme * fix: run prettier * fix: add content/.gitkeep commit0d3cf29226Author: Ben Schlegel <31989404+benschlegel@users.noreply.github.com> Date: Mon Sep 18 23:32:00 2023 +0200 docs: fix explorer example (#483) * feat: dynamically generate og images, write to fs as png * fix: og preview on discord * feat: use `sharp` to convert to webp, add content headers * feat: add config for theme (light or dark) * feat: improve image margins, add font breakpoint * feat: use config header + body fonts for satori * perf: memoize fonts * feat: use default og image if no path exists * feat: add config option for social images * feat: support custom og images via frontmatter * refactor: clean font helpers, rename fonts helper * refactor: make image generation cleaner * refactor: move default image to own component * chore: add todos * fix: only set width/height header if known * feat: remove html from description * feat: make image dimensions configurable * feat: pass userOpts to image generator * feat: option for users to provide own image struct (satori) This allows users to pass their own jsx for generating the default og image * refactor: rename `defaultImage.tsx` > `socialImage.tsx` * chore: improve comments + types * refactor: rename socialImage frontmatter property * feat: add frontmatter aliases for cover image * feat: add frontmatter alias for obsidian publish * docs: add documentation for social images * feat: add `generateSocialImages` prop to config * chore: update lock file * fix: fix type error * chore: update package.json * chore: update package-lock.json * docs: update docs Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * fix: clean url for use in metadata * refactor: clean function signature * feat: pass `fileData` to image generator * CI: run format * fix: file system import * fix: merge paths using `joinSegments` * fix: get output dir via `ctx.argv.output` * chore: add explanation to font regex * Squashed commit of the following: commit7164857f6eAuthor: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Fri Mar 15 21:17:42 2024 -0400 chore(ofm): remove unused (#999) Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> commit47024022e8Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri Mar 15 18:29:14 2024 -0400 chore(deps-dev): bump @types/node from 20.11.24 to 20.11.25 (#990) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.11.24 to 20.11.25. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commitb98e4be665Author: Mara-Li <lili.simonetti@outlook.fr> Date: Fri Mar 15 23:28:31 2024 +0100 feat(i18n): Add French translation for reading time (#998) Signed-off-by: Mara-Li <lili.simonetti@outlook.fr> commit8be51a0504Author: catcodeme <1020082805@qq.com> Date: Fri Mar 15 14:25:01 2024 +0800 fix: wikiLink in table (#993) * fix: wikiLink in table - update regexp to make '\' to group in alias - handle alias using block_id * style: format with prettier * style: add comment for block_ref(without alias) in table --------- Co-authored-by: hulinjiang <hulinjiang@58.com> commit92cc23dc45Author: Linus Sehn <37184648+linozen@users.noreply.github.com> Date: Wed Mar 13 08:59:37 2024 +0100 feat(plugin): citations (#984) * feat: add rehype-citations * feat: add citations transformer plugin * feat: add rehype-rewrite * feat: add csl option and add no-popover to citation links * revert: add rehype-rewrite 04b2692 'feat: add rehype-rewrite' * feat: use existing package for html manipulation * fix: remove `console.log()` commit097abc3cdaAuthor: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Mar 11 13:41:48 2024 -0700 chore(deps): bump async-mutex from 0.4.1 to 0.5.0 (#991) Bumps [async-mutex](https://github.com/DirtyHairy/async-mutex) from 0.4.1 to 0.5.0. - [Changelog](https://github.com/DirtyHairy/async-mutex/blob/master/CHANGELOG.md) - [Commits](https://github.com/DirtyHairy/async-mutex/compare/v0.4.1...v0.5.0) --- updated-dependencies: - dependency-name: async-mutex dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commita00324ddfdAuthor: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Mar 11 13:41:41 2024 -0700 chore(deps-dev): bump typescript from 5.3.3 to 5.4.2 (#989) Bumps [typescript](https://github.com/Microsoft/TypeScript) from 5.3.3 to 5.4.2. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml) - [Commits](https://github.com/Microsoft/TypeScript/compare/v5.3.3...v5.4.2) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit9fff6d7d0dAuthor: Mara-Li <lili.simonetti@outlook.fr> Date: Mon Mar 11 17:46:53 2024 +0100 fix: spelling error (#987) I really don't know why I translated this like that into "pas trouvé", and it bugged me a lot. I finally fixed it… Signed-off-by: Mara-Li <lili.simonetti@outlook.fr> commit0f5a9d7b66Author: Matt Vogel <mainmoniker@googlemail.com> Date: Sun Mar 10 12:57:10 2024 -0400 feat: separated content meta (#929) to allow for CSS styling commitb4236e5142Author: kabirgh <15871468+kabirgh@users.noreply.github.com> Date: Sun Mar 10 00:42:23 2024 +0000 feat(perf:fast-rebuilds): Stop mutating resources param in ComponentResources emitter (#977) * Stop mutating resources param in ComponentResources emitter * Add done rebuilding log for fast rebuilds * Move google font loading to Head component * Simplify code and fix comment commit6e0c102970Author: Emile Bangma <ewjbangma@hotmail.com> Date: Sun Mar 10 01:14:31 2024 +0100 fix(transclusion): prevent duplicate transclusion if multiple transclusions are present. (#982) commit94a54698abAuthor: Emile Bangma <ewjbangma@hotmail.com> Date: Sat Mar 9 17:59:55 2024 +0100 fix(resources): Use full path to font when cdnCache is false (#976) commit2e9a0c21dbAuthor: Emile Bangma <ewjbangma@hotmail.com> Date: Sat Mar 9 17:43:40 2024 +0100 fix(description): first sentence no longer repeats until max length (#981) commitb30a200bd4Author: Aaron Pham <29749331+aarnphm@users.noreply.github.com> Date: Fri Mar 8 12:14:22 2024 -0500 fix(i18n): make sure to use correct fileData for manual localization (#975) Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> commit6d59aa8201Author: Emile Bangma <ewjbangma@hotmail.com> Date: Fri Mar 8 10:04:44 2024 +0100 fix(description): counts characters instead of words (#972) * fix(description): make sure description counts characters instead of words * ref: removed duplicate ternary * CI: fix package log post merge * CI: fix more merge artifacts * CI: fix package-lock.json * feat: add new default image template * feat: use icon.png for image generation * chore: update satori and sharp version * feat(image-generator): add new default template * Update quartz/components/Head.tsx * Update quartz/components/Head.tsx * Update quartz/components/Head.tsx * Update docs/features/social images.md * Update quartz/components/Head.tsx Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * feat(og-image): add config option to use default og image for root path * docs(og-image): add `excludeRoot` config + update preview images * docs(open-graph): add examples section * chore: remove unused `socialImage2.tsx` component * feat(open-graph): add frontmatter aliases for socialImage/cover/image * fix(open-graph): only load satori font if config option is enabled * refactor(open-graph): dont use async promise inside `fetchTtf()` * chore: renaming and finished copywriting Signed-off-by: Aaron Pham <contact@aarnphm.xyz> * chore: update typo Signed-off-by: Aaron Pham <contact@aarnphm.xyz> * chore: update hinting for socialImage Signed-off-by: Aaron Pham <contact@aarnphm.xyz> --------- Signed-off-by: Aaron Pham <contact@aarnphm.xyz> Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> Co-authored-by: Emile Bangma <ewjbangma@hotmail.com> Co-authored-by: Emile Bangma <github@emilebangma.com> Co-authored-by: Aaron Pham <contact@aarnphm.xyz>
This commit is contained in:
		@@ -6,3 +6,12 @@ export const escapeHTML = (unsafe: string) => {
 | 
			
		||||
    .replaceAll('"', """)
 | 
			
		||||
    .replaceAll("'", "'")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const unescapeHTML = (html: string) => {
 | 
			
		||||
  return html
 | 
			
		||||
    .replaceAll("&", "&")
 | 
			
		||||
    .replaceAll("<", "<")
 | 
			
		||||
    .replaceAll(">", ">")
 | 
			
		||||
    .replaceAll(""", '"')
 | 
			
		||||
    .replaceAll("'", "'")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										200
									
								
								quartz/util/og.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								quartz/util/og.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,200 @@
 | 
			
		||||
import { FontWeight, SatoriOptions } from "satori/wasm"
 | 
			
		||||
import { GlobalConfiguration } from "../cfg"
 | 
			
		||||
import { QuartzPluginData } from "../plugins/vfile"
 | 
			
		||||
import { JSXInternal } from "preact/src/jsx"
 | 
			
		||||
import { ThemeKey } from "./theme"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get an array of `FontOptions` (for satori) given google font names
 | 
			
		||||
 * @param headerFontName name of google font used for header
 | 
			
		||||
 * @param bodyFontName name of google font used for body
 | 
			
		||||
 * @returns FontOptions for header and body
 | 
			
		||||
 */
 | 
			
		||||
export async function getSatoriFont(headerFontName: string, bodyFontName: string) {
 | 
			
		||||
  const headerWeight = 700 as FontWeight
 | 
			
		||||
  const bodyWeight = 400 as FontWeight
 | 
			
		||||
 | 
			
		||||
  // Fetch fonts
 | 
			
		||||
  const headerFont = await fetchTtf(headerFontName, headerWeight)
 | 
			
		||||
  const bodyFont = await fetchTtf(bodyFontName, bodyWeight)
 | 
			
		||||
 | 
			
		||||
  // Convert fonts to satori font format and return
 | 
			
		||||
  const fonts: SatoriOptions["fonts"] = [
 | 
			
		||||
    { name: headerFontName, data: headerFont, weight: headerWeight, style: "normal" },
 | 
			
		||||
    { name: bodyFontName, data: bodyFont, weight: bodyWeight, style: "normal" },
 | 
			
		||||
  ]
 | 
			
		||||
  return fonts
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get the `.ttf` file of a google font
 | 
			
		||||
 * @param fontName name of google font
 | 
			
		||||
 * @param weight what font weight to fetch font
 | 
			
		||||
 * @returns `.ttf` file of google font
 | 
			
		||||
 */
 | 
			
		||||
async function fetchTtf(fontName: string, weight: FontWeight): Promise<ArrayBuffer> {
 | 
			
		||||
  try {
 | 
			
		||||
    // Get css file from google fonts
 | 
			
		||||
    const cssResponse = await fetch(`https://fonts.googleapis.com/css?family=${fontName}:${weight}`)
 | 
			
		||||
    const css = await cssResponse.text()
 | 
			
		||||
 | 
			
		||||
    // Extract .ttf url from css file
 | 
			
		||||
    const urlRegex = /url\((https:\/\/fonts.gstatic.com\/s\/.*?.ttf)\)/g
 | 
			
		||||
    const match = urlRegex.exec(css)
 | 
			
		||||
 | 
			
		||||
    if (!match) {
 | 
			
		||||
      throw new Error("Could not fetch font")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Retrieve font data as ArrayBuffer
 | 
			
		||||
    const fontResponse = await fetch(match[1])
 | 
			
		||||
 | 
			
		||||
    // fontData is an ArrayBuffer containing the .ttf file data (get match[1] due to google fonts response format, always contains link twice, but second entry is the "raw" link)
 | 
			
		||||
    const fontData = await fontResponse.arrayBuffer()
 | 
			
		||||
 | 
			
		||||
    return fontData
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    throw new Error(`Error fetching font: ${error}`)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type SocialImageOptions = {
 | 
			
		||||
  /**
 | 
			
		||||
   * What color scheme to use for image generation (uses colors from config theme)
 | 
			
		||||
   */
 | 
			
		||||
  colorScheme: ThemeKey
 | 
			
		||||
  /**
 | 
			
		||||
   * Height to generate image with in pixels (should be around 630px)
 | 
			
		||||
   */
 | 
			
		||||
  height: number
 | 
			
		||||
  /**
 | 
			
		||||
   * Width to generate image with in pixels (should be around 1200px)
 | 
			
		||||
   */
 | 
			
		||||
  width: number
 | 
			
		||||
  /**
 | 
			
		||||
   * Whether to use the auto generated image for the root path ("/", when set to false) or the default og image (when set to true).
 | 
			
		||||
   */
 | 
			
		||||
  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,
 | 
			
		||||
  ) => JSXInternal.Element
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type UserOpts = Omit<SocialImageOptions, "imageStructure">
 | 
			
		||||
 | 
			
		||||
export type ImageOptions = {
 | 
			
		||||
  /**
 | 
			
		||||
   * what title to use as header in image
 | 
			
		||||
   */
 | 
			
		||||
  title: string
 | 
			
		||||
  /**
 | 
			
		||||
   * what description to use as body in image
 | 
			
		||||
   */
 | 
			
		||||
  description: string
 | 
			
		||||
  /**
 | 
			
		||||
   * what fileName to use when writing to disk
 | 
			
		||||
   */
 | 
			
		||||
  fileName: string
 | 
			
		||||
  /**
 | 
			
		||||
   * what directory to store image in
 | 
			
		||||
   */
 | 
			
		||||
  fileDir: string
 | 
			
		||||
  /**
 | 
			
		||||
   * what file extension to use (should be `webp` unless you also change sharp conversion)
 | 
			
		||||
   */
 | 
			
		||||
  fileExt: string
 | 
			
		||||
  /**
 | 
			
		||||
   * header + body font to be used when generating satori image (as promise to work around sync in component)
 | 
			
		||||
   */
 | 
			
		||||
  fontsPromise: Promise<SatoriOptions["fonts"]>
 | 
			
		||||
  /**
 | 
			
		||||
   * `GlobalConfiguration` of quartz (used for theme/typography)
 | 
			
		||||
   */
 | 
			
		||||
  cfg: GlobalConfiguration
 | 
			
		||||
  /**
 | 
			
		||||
   * full file data of current page
 | 
			
		||||
   */
 | 
			
		||||
  fileData: QuartzPluginData
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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,
 | 
			
		||||
) => {
 | 
			
		||||
  // How many characters are allowed before switching to smaller font
 | 
			
		||||
  const fontBreakPoint = 22
 | 
			
		||||
  const useSmallerFont = title.length > fontBreakPoint
 | 
			
		||||
 | 
			
		||||
  // Setup to access image
 | 
			
		||||
  const iconPath = `https://${cfg.baseUrl}/static/icon.png`
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      style={{
 | 
			
		||||
        display: "flex",
 | 
			
		||||
        flexDirection: "column",
 | 
			
		||||
        justifyContent: "center",
 | 
			
		||||
        alignItems: "center",
 | 
			
		||||
        height: "100%",
 | 
			
		||||
        width: "100%",
 | 
			
		||||
        backgroundColor: cfg.theme.colors[colorScheme].light,
 | 
			
		||||
        gap: "2rem",
 | 
			
		||||
        paddingTop: "1.5rem",
 | 
			
		||||
        paddingBottom: "1.5rem",
 | 
			
		||||
        paddingLeft: "5rem",
 | 
			
		||||
        paddingRight: "5rem",
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <div
 | 
			
		||||
        style={{
 | 
			
		||||
          display: "flex",
 | 
			
		||||
          alignItems: "center",
 | 
			
		||||
          justifyContent: "flex-start",
 | 
			
		||||
          width: "100%",
 | 
			
		||||
          flexDirection: "row",
 | 
			
		||||
          gap: "2.5rem",
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <img src={iconPath} width={135} height={135} />
 | 
			
		||||
        <p
 | 
			
		||||
          style={{
 | 
			
		||||
            color: cfg.theme.colors[colorScheme].dark,
 | 
			
		||||
            fontSize: useSmallerFont ? 70 : 82,
 | 
			
		||||
            fontFamily: fonts[0].name,
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          {title}
 | 
			
		||||
        </p>
 | 
			
		||||
      </div>
 | 
			
		||||
      <p
 | 
			
		||||
        style={{
 | 
			
		||||
          color: cfg.theme.colors[colorScheme].dark,
 | 
			
		||||
          fontSize: 44,
 | 
			
		||||
          lineClamp: 3,
 | 
			
		||||
          fontFamily: fonts[1].name,
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        {description}
 | 
			
		||||
      </p>
 | 
			
		||||
    </div>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user