- {excerpt}
- {excerpt && "..."}
+
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+ {post.cover && (
+

+ )}
diff --git a/src/components/site/SiteLayout.tsx b/src/components/site/SiteLayout.tsx
index e062aea9..a0ba769e 100644
--- a/src/components/site/SiteLayout.tsx
+++ b/src/components/site/SiteLayout.tsx
@@ -45,7 +45,7 @@ export const SiteLayout: React.FC
= ({
description={
ogDescription ?? site?.description?.replace(/<[^>]*>/g, "")
}
- image={getUserContentsUrl(site?.avatars?.[0])}
+ image={page?.cover || getUserContentsUrl(site?.avatars?.[0])}
/>
diff --git a/src/lib/types.ts b/src/lib/types.ts
index 0db7beac..95735525 100644
--- a/src/lib/types.ts
+++ b/src/lib/types.ts
@@ -74,6 +74,7 @@ export type SiteNavigationItem = {
export type Note = UniNote & {
slug?: string
character?: Profile
+ cover?: string
}
export type Notes = {
diff --git a/src/markdown/index.ts b/src/markdown/index.ts
index fa507217..7d9b7a09 100644
--- a/src/markdown/index.ts
+++ b/src/markdown/index.ts
@@ -20,12 +20,14 @@ export type MarkdownEnv = {
excerpt: string
frontMatter: Record
__internal: Record
+ cover: string
}
export type Rendered = {
contentHTML: string
excerpt: string
frontMatter: Record
+ cover: string
}
refractor.alias("html", ["svelte", "vue"])
@@ -33,7 +35,12 @@ refractor.alias("html", ["svelte", "vue"])
const rehypePrism = rehypePrismGenerator(refractor)
export const renderPageContent = async (content: string): Promise => {
- const env: MarkdownEnv = { excerpt: "", __internal: {}, frontMatter: {} }
+ const env: MarkdownEnv = {
+ excerpt: "",
+ __internal: {},
+ frontMatter: {},
+ cover: "",
+ }
const result = await unified()
.use(remarkParse)
@@ -55,7 +62,7 @@ export const renderPageContent = async (content: string): Promise => {
.use(remarkCallout)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeRaw)
- .use(rehypeImage)
+ .use(rehypeImage, { env })
.use(rehypeSanitize, {
...defaultSchema,
tagNames: [...(defaultSchema.tagNames || []), "video", "iframe"],
@@ -94,5 +101,10 @@ export const renderPageContent = async (content: string): Promise => {
const contentHTML = result.toString()
- return { contentHTML, excerpt: env.excerpt, frontMatter: env.frontMatter }
+ return {
+ contentHTML,
+ excerpt: env.excerpt,
+ frontMatter: env.frontMatter,
+ cover: env.cover,
+ }
}
diff --git a/src/markdown/rehype-image.ts b/src/markdown/rehype-image.ts
index 613a8119..dadd4958 100644
--- a/src/markdown/rehype-image.ts
+++ b/src/markdown/rehype-image.ts
@@ -3,11 +3,15 @@ import { Plugin } from "unified"
import { visit } from "unist-util-visit"
import { getUserContentsUrl } from "~/lib/user-contents"
import { toGateway } from "~/lib/ipfs-parser"
+import { MarkdownEnv } from "."
const isExternLink = (url: string) => /^https?:\/\//.test(url)
-export const rehypeImage: Plugin, Root> = () => {
+export const rehypeImage: Plugin, Root> = ({
+ env,
+}) => {
return (tree) => {
+ let first = true
visit(tree, { tagName: "img" }, (node) => {
if (!node.properties) return
@@ -21,6 +25,11 @@ export const rehypeImage: Plugin, Root> = () => {
node.properties.src = ipfsUrl
url = ipfsUrl
+ if (first) {
+ env.cover = url
+ first = false
+ }
+
if (isExternLink(url)) {
if (!url.startsWith("https:")) {
throw new Error(`External image url must start with https`)
diff --git a/src/models/page.model.ts b/src/models/page.model.ts
index 3263726f..1a78c269 100644
--- a/src/models/page.model.ts
+++ b/src/models/page.model.ts
@@ -131,6 +131,7 @@ const expandPage = async (page: Note, render: boolean) => {
mime_type: "text/html",
}
}
+ page.cover = rendered.cover
}
page.slug =
page.attributes?.find((a) => a.trait_type === "xlog_slug")?.value ||