diff --git a/src/components/site/SiteHome.tsx b/src/components/site/SiteHome.tsx index 27c3f7db..2b209eaf 100644 --- a/src/components/site/SiteHome.tsx +++ b/src/components/site/SiteHome.tsx @@ -17,16 +17,28 @@ export const SiteHome: React.FC<{ const excerpt = post.summary?.content return ( - -

- {post.title} -

-
- {formatDate(post.date_published)} + +
+

+ {post.title} +

+
+ {formatDate(post.date_published)} +
+
+ {excerpt} + {excerpt && "..."} +
-
- {excerpt} - {excerpt && "..."} +
+ {/* eslint-disable-next-line @next/next/no-img-element */} + {post.cover && ( + 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 ||