mirror of
https://github.com/ChenQihan666/MyBlog-Next.git
synced 2026-08-14 07:33:07 +08:00
refactor!: remove react dependency for UI interactions (#457)
Remove react as a dependency for UI interactions. Update jsx components such as Search and Datetime with astro components.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
---
|
||||
import { slugifyStr } from "@/utils/slugify";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import Datetime from "./Datetime.astro";
|
||||
|
||||
export interface Props {
|
||||
href?: string;
|
||||
frontmatter: CollectionEntry<"blog">["data"];
|
||||
secHeading?: boolean;
|
||||
}
|
||||
|
||||
const { href, frontmatter, secHeading = true } = Astro.props;
|
||||
|
||||
const { title, pubDatetime, modDatetime, description } = frontmatter;
|
||||
|
||||
const headerProps = {
|
||||
style: { viewTransitionName: slugifyStr(title) },
|
||||
class: "text-lg font-medium decoration-dashed hover:underline",
|
||||
};
|
||||
---
|
||||
|
||||
<li class="my-6">
|
||||
<a
|
||||
href={href}
|
||||
class="inline-block text-lg font-medium text-accent decoration-dashed underline-offset-4 focus-visible:no-underline focus-visible:underline-offset-0"
|
||||
>
|
||||
{
|
||||
secHeading ? (
|
||||
<h2 {...headerProps}>{title}</h2>
|
||||
) : (
|
||||
<h3 {...headerProps}>{title}</h3>
|
||||
)
|
||||
}
|
||||
</a>
|
||||
<Datetime pubDatetime={pubDatetime} modDatetime={modDatetime} />
|
||||
<p>{description}</p>
|
||||
</li>
|
||||
@@ -1,35 +0,0 @@
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { slugifyStr } from "@/utils/slugify";
|
||||
import { Datetime } from "./Datetime";
|
||||
|
||||
export interface Props {
|
||||
href?: string;
|
||||
frontmatter: CollectionEntry<"blog">["data"];
|
||||
secHeading?: boolean;
|
||||
}
|
||||
|
||||
export default function Card({ href, frontmatter, secHeading = true }: Props) {
|
||||
const { title, pubDatetime, modDatetime, description } = frontmatter;
|
||||
|
||||
const headerProps = {
|
||||
style: { viewTransitionName: slugifyStr(title) },
|
||||
className: "text-lg font-medium decoration-dashed hover:underline",
|
||||
};
|
||||
|
||||
return (
|
||||
<li className="my-6">
|
||||
<a
|
||||
href={href}
|
||||
className="inline-block text-lg font-medium text-accent decoration-dashed underline-offset-4 focus-visible:no-underline focus-visible:underline-offset-0"
|
||||
>
|
||||
{secHeading ? (
|
||||
<h2 {...headerProps}>{title}</h2>
|
||||
) : (
|
||||
<h3 {...headerProps}>{title}</h3>
|
||||
)}
|
||||
</a>
|
||||
<Datetime pubDatetime={pubDatetime} modDatetime={modDatetime} />
|
||||
<p>{description}</p>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
import { LOCALE } from "@/constants";
|
||||
|
||||
export interface Props {
|
||||
pubDatetime: string | Date;
|
||||
modDatetime: string | Date | undefined | null;
|
||||
size?: "sm" | "lg";
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
pubDatetime,
|
||||
modDatetime,
|
||||
size = "sm",
|
||||
class: className = "",
|
||||
} = Astro.props;
|
||||
|
||||
/* ========== Formatted Datetime ========== */
|
||||
const myDatetime = new Date(
|
||||
modDatetime && modDatetime > pubDatetime ? modDatetime : pubDatetime
|
||||
);
|
||||
const date = myDatetime.toLocaleDateString(LOCALE.langTag, {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
|
||||
const time = myDatetime.toLocaleTimeString(LOCALE.langTag, {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
---
|
||||
|
||||
<div class={`flex items-end space-x-2 opacity-80 ${className}`.trim()}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={`${
|
||||
size === "sm" ? "scale-90" : "scale-100"
|
||||
} inline-block h-6 w-6 min-w-[1.375rem] fill-foreground`}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M7 11h2v2H7zm0 4h2v2H7zm4-4h2v2h-2zm0 4h2v2h-2zm4-4h2v2h-2zm0 4h2v2h-2z"
|
||||
></path>
|
||||
<path
|
||||
d="M5 22h14c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2h-2V2h-2v2H9V2H7v2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2zM19 8l.001 12H5V8h14z"
|
||||
></path>
|
||||
</svg>
|
||||
{
|
||||
modDatetime && modDatetime > pubDatetime ? (
|
||||
<span
|
||||
class={`italic ${size === "sm" ? "text-sm" : "text-sm sm:text-base"}`}
|
||||
>
|
||||
Updated:
|
||||
</span>
|
||||
) : (
|
||||
<span class="sr-only">Published:</span>
|
||||
)
|
||||
}
|
||||
<span class={`italic ${size === "sm" ? "text-sm" : "text-sm sm:text-base"}`}>
|
||||
<time datetime={myDatetime.toISOString()}>{date}</time>
|
||||
<span aria-hidden="true"> | </span>
|
||||
<span class="sr-only"> at </span>
|
||||
<span class="text-nowrap">{time}</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -1,76 +0,0 @@
|
||||
import { LOCALE } from "@/constants";
|
||||
|
||||
interface DatetimesProps {
|
||||
pubDatetime: string | Date;
|
||||
modDatetime: string | Date | undefined | null;
|
||||
}
|
||||
|
||||
interface Props extends DatetimesProps {
|
||||
size?: "sm" | "lg";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Datetime({
|
||||
pubDatetime,
|
||||
modDatetime,
|
||||
size = "sm",
|
||||
className = "",
|
||||
}: Props) {
|
||||
return (
|
||||
<div className={`flex items-end space-x-2 opacity-80 ${className}`.trim()}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`${
|
||||
size === "sm" ? "scale-90" : "scale-100"
|
||||
} inline-block h-6 w-6 min-w-[1.375rem] fill-foreground`}
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M7 11h2v2H7zm0 4h2v2H7zm4-4h2v2h-2zm0 4h2v2h-2zm4-4h2v2h-2zm0 4h2v2h-2z"></path>
|
||||
<path d="M5 22h14c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2h-2V2h-2v2H9V2H7v2H5c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2zM19 8l.001 12H5V8h14z"></path>
|
||||
</svg>
|
||||
{modDatetime && modDatetime > pubDatetime ? (
|
||||
<span
|
||||
className={`italic ${size === "sm" ? "text-sm" : "text-sm sm:text-base"}`}
|
||||
>
|
||||
Updated:
|
||||
</span>
|
||||
) : (
|
||||
<span className="sr-only">Published:</span>
|
||||
)}
|
||||
<span
|
||||
className={`italic ${size === "sm" ? "text-sm" : "text-sm sm:text-base"}`}
|
||||
>
|
||||
<FormattedDatetime
|
||||
pubDatetime={pubDatetime}
|
||||
modDatetime={modDatetime}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const FormattedDatetime = ({ pubDatetime, modDatetime }: DatetimesProps) => {
|
||||
const myDatetime = new Date(
|
||||
modDatetime && modDatetime > pubDatetime ? modDatetime : pubDatetime
|
||||
);
|
||||
|
||||
const date = myDatetime.toLocaleDateString(LOCALE.langTag, {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
|
||||
const time = myDatetime.toLocaleTimeString(LOCALE.langTag, {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<time dateTime={myDatetime.toISOString()}>{date}</time>
|
||||
<span aria-hidden="true"> | </span>
|
||||
<span className="sr-only"> at </span>
|
||||
<span className="text-nowrap">{time}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,135 +0,0 @@
|
||||
import Fuse from "fuse.js";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { useEffect, useRef, useState, useMemo, type FormEvent } from "react";
|
||||
import Card from "@/components/Card";
|
||||
|
||||
export type SearchItem = {
|
||||
title: string;
|
||||
description: string;
|
||||
data: CollectionEntry<"blog">["data"];
|
||||
slug: string;
|
||||
};
|
||||
|
||||
interface Props {
|
||||
searchList: SearchItem[];
|
||||
backUrl: string;
|
||||
}
|
||||
|
||||
interface SearchResult {
|
||||
item: SearchItem;
|
||||
refIndex: number;
|
||||
}
|
||||
|
||||
export default function SearchBar({ searchList, backUrl }: Props) {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [inputVal, setInputVal] = useState("");
|
||||
const [searchResults, setSearchResults] = useState<SearchResult[] | null>(
|
||||
null
|
||||
);
|
||||
|
||||
const handleChange = (e: FormEvent<HTMLInputElement>) => {
|
||||
setInputVal(e.currentTarget.value);
|
||||
};
|
||||
|
||||
const fuse = useMemo(
|
||||
() =>
|
||||
new Fuse(searchList, {
|
||||
keys: ["title", "description"],
|
||||
includeMatches: true,
|
||||
minMatchCharLength: 2,
|
||||
threshold: 0.5,
|
||||
}),
|
||||
[searchList]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// if URL has search query,
|
||||
// insert that search query in input field
|
||||
const searchUrl = new URLSearchParams(window.location.search);
|
||||
const searchStr = searchUrl.get("q");
|
||||
if (searchStr) setInputVal(searchStr);
|
||||
|
||||
// put focus cursor at the end of the string
|
||||
setTimeout(function () {
|
||||
inputRef.current!.selectionStart = inputRef.current!.selectionEnd =
|
||||
searchStr?.length || 0;
|
||||
}, 50);
|
||||
}, []);
|
||||
|
||||
let newRelativePathQuery = "";
|
||||
useEffect(() => {
|
||||
// Add search result only if
|
||||
// input value is more than one character
|
||||
const inputResult = inputVal.length > 1 ? fuse.search(inputVal) : [];
|
||||
setSearchResults(inputResult);
|
||||
|
||||
// Update search string in URL
|
||||
if (inputVal.length > 0) {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
searchParams.set("q", inputVal);
|
||||
newRelativePathQuery =
|
||||
window.location.pathname + "?" + searchParams.toString();
|
||||
history.replaceState(history.state, "", newRelativePathQuery);
|
||||
} else {
|
||||
history.replaceState(history.state, "", window.location.pathname);
|
||||
}
|
||||
}, [inputVal]);
|
||||
|
||||
useEffect(() => {
|
||||
// focus on text input when search bar is displayed
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}, [inputVal]);
|
||||
|
||||
const fullBackUrl = `${backUrl}${inputVal.trim() !== "" ? `?q=${inputVal}` : "/"}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<label className="relative block">
|
||||
<span className="sr-only">Search</span>
|
||||
<span className="absolute inset-y-0 left-0 flex items-center pl-2 opacity-75">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
className="inline-block size-6 fill-foreground"
|
||||
>
|
||||
<path d="M19.023 16.977a35.13 35.13 0 0 1-1.367-1.384c-.372-.378-.596-.653-.596-.653l-2.8-1.337A6.962 6.962 0 0 0 16 9c0-3.859-3.14-7-7-7S2 5.141 2 9s3.14 7 7 7c1.763 0 3.37-.66 4.603-1.739l1.337 2.8s.275.224.653.596c.387.363.896.854 1.384 1.367l1.358 1.392.604.646 2.121-2.121-.646-.604c-.379-.372-.885-.866-1.391-1.36zM9 14c-2.757 0-5-2.243-5-5s2.243-5 5-5 5 2.243 5 5-2.243 5-5 5z"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<input
|
||||
className="border-skin-fill/40 bg-skin-fill focus:border-skin-accent block w-full rounded border py-3 pr-3 pl-10 placeholder:italic focus:outline-none"
|
||||
placeholder="Search for anything..."
|
||||
type="text"
|
||||
name="search"
|
||||
value={inputVal}
|
||||
onChange={handleChange}
|
||||
autoComplete="off"
|
||||
// autoFocus
|
||||
ref={inputRef}
|
||||
/>
|
||||
</label>
|
||||
|
||||
{inputVal.length > 1 && (
|
||||
<div className="mt-8">
|
||||
Found {searchResults?.length}
|
||||
{searchResults?.length && searchResults?.length === 1
|
||||
? " result"
|
||||
: " results"}{" "}
|
||||
for '{inputVal}'
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ul>
|
||||
{searchResults &&
|
||||
searchResults.map(({ item, refIndex }) => (
|
||||
<Card
|
||||
href={`/posts/${item.slug}${fullBackUrl}`}
|
||||
frontmatter={item.data}
|
||||
key={`${refIndex}-${item.slug}`}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import Layout from "@/layouts/Layout.astro";
|
||||
import Header from "@/components/Header.astro";
|
||||
import Footer from "@/components/Footer.astro";
|
||||
import Tag from "@/components/Tag.astro";
|
||||
import { Datetime } from "@/components/Datetime";
|
||||
import Datetime from "@/components/Datetime.astro";
|
||||
import EditPost from "@/components/EditPost.astro";
|
||||
import ShareLinks from "@/components/ShareLinks.astro";
|
||||
import BackButton from "@/components/BackButton.astro";
|
||||
@@ -86,7 +86,7 @@ const nextPost =
|
||||
pubDatetime={pubDatetime}
|
||||
modDatetime={modDatetime}
|
||||
size="lg"
|
||||
className="my-2"
|
||||
class="my-2"
|
||||
/>
|
||||
<EditPost class="max-sm:hidden" editPost={editPost} postId={post.id} />
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import Main from "@/layouts/Main.astro";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import Header from "@/components/Header.astro";
|
||||
import Footer from "@/components/Footer.astro";
|
||||
import Card from "@/components/Card";
|
||||
import Card from "@/components/Card.astro";
|
||||
import getPostsByGroupCondition from "@/utils/getPostsByGroupCondition";
|
||||
import { SITE } from "@/config";
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import Header from "@/components/Header.astro";
|
||||
import Footer from "@/components/Footer.astro";
|
||||
import Socials from "@/components/Socials.astro";
|
||||
import LinkButton from "@/components/LinkButton.astro";
|
||||
import Card from "@/components/Card";
|
||||
import Card from "@/components/Card.astro";
|
||||
import Hr from "@/components/Hr.astro";
|
||||
import getSortedPosts from "@/utils/getSortedPosts";
|
||||
import IconRss from "@/assets/icons/IconRss.svg";
|
||||
|
||||
@@ -5,7 +5,7 @@ import Main from "@/layouts/Main.astro";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import Header from "@/components/Header.astro";
|
||||
import Footer from "@/components/Footer.astro";
|
||||
import Card from "@/components/Card";
|
||||
import Card from "@/components/Card.astro";
|
||||
import Pagination from "@/components/Pagination.astro";
|
||||
import getSortedPosts from "@/utils/getSortedPosts";
|
||||
import { SITE } from "@/config";
|
||||
|
||||
+1
-18
@@ -1,32 +1,15 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Main from "@/layouts/Main.astro";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import Header from "@/components/Header.astro";
|
||||
import Footer from "@/components/Footer.astro";
|
||||
import SearchBar from "@/components/SearchBar";
|
||||
import getSortedPosts from "@/utils/getSortedPosts";
|
||||
import { SITE } from "@/config";
|
||||
|
||||
// Retrieve all published articles
|
||||
const posts = await getCollection("blog", ({ data }) => !data.draft);
|
||||
const sortedPosts = getSortedPosts(posts);
|
||||
|
||||
// List of items to search in
|
||||
const searchList = sortedPosts.map(({ data, id }) => ({
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
data,
|
||||
slug: id,
|
||||
}));
|
||||
|
||||
const backUrl = SITE.showBackButton ? `?from=${Astro.url.pathname}` : "/";
|
||||
---
|
||||
|
||||
<Layout title={`Search | ${SITE.title}`}>
|
||||
<Header />
|
||||
<Main pageTitle="Search" pageDesc="Search any article ...">
|
||||
<SearchBar client:load searchList={searchList} backUrl={backUrl} />
|
||||
<!-- Search bar -->
|
||||
</Main>
|
||||
<Footer />
|
||||
</Layout>
|
||||
|
||||
@@ -5,7 +5,7 @@ import Main from "@/layouts/Main.astro";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import Header from "@/components/Header.astro";
|
||||
import Footer from "@/components/Footer.astro";
|
||||
import Card from "@/components/Card";
|
||||
import Card from "@/components/Card.astro";
|
||||
import Pagination from "@/components/Pagination.astro";
|
||||
import getUniqueTags from "@/utils/getUniqueTags";
|
||||
import getPostsByTag from "@/utils/getPostsByTag";
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
import type { FontStyle, FontWeight } from "satori";
|
||||
|
||||
export type FontOptions = {
|
||||
name: string;
|
||||
data: ArrayBuffer;
|
||||
weight: FontWeight | undefined;
|
||||
style: FontStyle | undefined;
|
||||
};
|
||||
|
||||
async function loadGoogleFont(
|
||||
font: string,
|
||||
text: string
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
import satori from "satori";
|
||||
// import { html } from "satori-html";
|
||||
import { SITE } from "@/config";
|
||||
import loadGoogleFonts from "../loadGoogleFont";
|
||||
|
||||
// const markup = html`<div
|
||||
// style={{
|
||||
// background: "#fefbfb",
|
||||
// width: "100%",
|
||||
// height: "100%",
|
||||
// display: "flex",
|
||||
// alignItems: "center",
|
||||
// justifyContent: "center",
|
||||
// }}
|
||||
// >
|
||||
// <div
|
||||
// style={{
|
||||
// position: "absolute",
|
||||
// top: "-1px",
|
||||
// right: "-1px",
|
||||
// border: "4px solid #000",
|
||||
// background: "#ecebeb",
|
||||
// opacity: "0.9",
|
||||
// borderRadius: "4px",
|
||||
// display: "flex",
|
||||
// justifyContent: "center",
|
||||
// margin: "2.5rem",
|
||||
// width: "88%",
|
||||
// height: "80%",
|
||||
// }}
|
||||
// />
|
||||
|
||||
// <div
|
||||
// style={{
|
||||
// border: "4px solid #000",
|
||||
// background: "#fefbfb",
|
||||
// borderRadius: "4px",
|
||||
// display: "flex",
|
||||
// justifyContent: "center",
|
||||
// margin: "2rem",
|
||||
// width: "88%",
|
||||
// height: "80%",
|
||||
// }}
|
||||
// >
|
||||
// <div
|
||||
// style={{
|
||||
// display: "flex",
|
||||
// flexDirection: "column",
|
||||
// justifyContent: "space-between",
|
||||
// margin: "20px",
|
||||
// width: "90%",
|
||||
// height: "90%",
|
||||
// }}
|
||||
// >
|
||||
// <p
|
||||
// style={{
|
||||
// fontSize: 72,
|
||||
// fontWeight: "bold",
|
||||
// maxHeight: "84%",
|
||||
// overflow: "hidden",
|
||||
// }}
|
||||
// >
|
||||
// {post.data.title}
|
||||
// </p>
|
||||
// <div
|
||||
// style={{
|
||||
// display: "flex",
|
||||
// justifyContent: "space-between",
|
||||
// width: "100%",
|
||||
// marginBottom: "8px",
|
||||
// fontSize: 28,
|
||||
// }}
|
||||
// >
|
||||
// <span>
|
||||
// by{" "}
|
||||
// <span
|
||||
// style={{
|
||||
// color: "transparent",
|
||||
// }}
|
||||
// >
|
||||
// "
|
||||
// </span>
|
||||
// <span style={{ overflow: "hidden", fontWeight: "bold" }}>
|
||||
// {post.data.author}
|
||||
// </span>
|
||||
// </span>
|
||||
|
||||
// <span style={{ overflow: "hidden", fontWeight: "bold" }}>
|
||||
// {SITE.title}
|
||||
// </span>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>`;
|
||||
|
||||
export default async post => {
|
||||
return satori(
|
||||
{
|
||||
type: "div",
|
||||
props: {
|
||||
style: {
|
||||
background: "#fefbfb",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: "div",
|
||||
props: {
|
||||
style: {
|
||||
position: "absolute",
|
||||
top: "-1px",
|
||||
right: "-1px",
|
||||
border: "4px solid #000",
|
||||
background: "#ecebeb",
|
||||
opacity: "0.9",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
margin: "2.5rem",
|
||||
width: "88%",
|
||||
height: "80%",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "div",
|
||||
props: {
|
||||
style: {
|
||||
border: "4px solid #000",
|
||||
background: "#fefbfb",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
margin: "2rem",
|
||||
width: "88%",
|
||||
height: "80%",
|
||||
},
|
||||
children: {
|
||||
type: "div",
|
||||
props: {
|
||||
style: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
margin: "20px",
|
||||
width: "90%",
|
||||
height: "90%",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: "p",
|
||||
props: {
|
||||
style: {
|
||||
fontSize: 72,
|
||||
fontWeight: "bold",
|
||||
maxHeight: "84%",
|
||||
overflow: "hidden",
|
||||
},
|
||||
children: post.data.title,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "div",
|
||||
props: {
|
||||
style: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
marginBottom: "8px",
|
||||
fontSize: 28,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: "span",
|
||||
props: {
|
||||
children: [
|
||||
"by ",
|
||||
{
|
||||
type: "span",
|
||||
props: {
|
||||
style: { color: "transparent" },
|
||||
children: '"',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "span",
|
||||
props: {
|
||||
style: {
|
||||
overflow: "hidden",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
children: post.data.author,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "span",
|
||||
props: {
|
||||
style: { overflow: "hidden", fontWeight: "bold" },
|
||||
children: SITE.title,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
embedFont: true,
|
||||
fonts: await loadGoogleFonts(
|
||||
post.data.title + post.data.author + SITE.title + "by"
|
||||
),
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -1,106 +0,0 @@
|
||||
import satori from "satori";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { SITE } from "@/config";
|
||||
import loadGoogleFonts, { type FontOptions } from "../loadGoogleFont";
|
||||
|
||||
export default async (post: CollectionEntry<"blog">) => {
|
||||
return satori(
|
||||
<div
|
||||
style={{
|
||||
background: "#fefbfb",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "-1px",
|
||||
right: "-1px",
|
||||
border: "4px solid #000",
|
||||
background: "#ecebeb",
|
||||
opacity: "0.9",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
margin: "2.5rem",
|
||||
width: "88%",
|
||||
height: "80%",
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
border: "4px solid #000",
|
||||
background: "#fefbfb",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
margin: "2rem",
|
||||
width: "88%",
|
||||
height: "80%",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
margin: "20px",
|
||||
width: "90%",
|
||||
height: "90%",
|
||||
}}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
fontSize: 72,
|
||||
fontWeight: "bold",
|
||||
maxHeight: "84%",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
{post.data.title}
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
width: "100%",
|
||||
marginBottom: "8px",
|
||||
fontSize: 28,
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
by{" "}
|
||||
<span
|
||||
style={{
|
||||
color: "transparent",
|
||||
}}
|
||||
>
|
||||
"
|
||||
</span>
|
||||
<span style={{ overflow: "hidden", fontWeight: "bold" }}>
|
||||
{post.data.author}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span style={{ overflow: "hidden", fontWeight: "bold" }}>
|
||||
{SITE.title}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
embedFont: true,
|
||||
fonts: (await loadGoogleFonts(
|
||||
post.data.title + post.data.author + SITE.title + "by"
|
||||
)) as FontOptions[],
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,128 @@
|
||||
import satori from "satori";
|
||||
import { SITE } from "@/config";
|
||||
import loadGoogleFonts from "../loadGoogleFont";
|
||||
|
||||
export default async () => {
|
||||
return satori(
|
||||
{
|
||||
type: "div",
|
||||
props: {
|
||||
style: {
|
||||
background: "#fefbfb",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: "div",
|
||||
props: {
|
||||
style: {
|
||||
position: "absolute",
|
||||
top: "-1px",
|
||||
right: "-1px",
|
||||
border: "4px solid #000",
|
||||
background: "#ecebeb",
|
||||
opacity: "0.9",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
margin: "2.5rem",
|
||||
width: "88%",
|
||||
height: "80%",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "div",
|
||||
props: {
|
||||
style: {
|
||||
border: "4px solid #000",
|
||||
background: "#fefbfb",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
margin: "2rem",
|
||||
width: "88%",
|
||||
height: "80%",
|
||||
},
|
||||
children: {
|
||||
type: "div",
|
||||
props: {
|
||||
style: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
margin: "20px",
|
||||
width: "90%",
|
||||
height: "90%",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: "div",
|
||||
props: {
|
||||
style: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "90%",
|
||||
maxHeight: "90%",
|
||||
overflow: "hidden",
|
||||
textAlign: "center",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: "p",
|
||||
props: {
|
||||
style: { fontSize: 72, fontWeight: "bold" },
|
||||
children: SITE.title,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "p",
|
||||
props: {
|
||||
style: { fontSize: 28 },
|
||||
children: SITE.desc,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "div",
|
||||
props: {
|
||||
style: {
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
width: "100%",
|
||||
marginBottom: "8px",
|
||||
fontSize: 28,
|
||||
},
|
||||
children: {
|
||||
type: "span",
|
||||
props: {
|
||||
style: { overflow: "hidden", fontWeight: "bold" },
|
||||
children: new URL(SITE.website).hostname,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
embedFont: true,
|
||||
fonts: await loadGoogleFonts(SITE.title + SITE.desc + SITE.website),
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -1,97 +0,0 @@
|
||||
import satori from "satori";
|
||||
import { SITE } from "@/config";
|
||||
import loadGoogleFonts, { type FontOptions } from "../loadGoogleFont";
|
||||
|
||||
export default async () => {
|
||||
return satori(
|
||||
<div
|
||||
style={{
|
||||
background: "#fefbfb",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "-1px",
|
||||
right: "-1px",
|
||||
border: "4px solid #000",
|
||||
background: "#ecebeb",
|
||||
opacity: "0.9",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
margin: "2.5rem",
|
||||
width: "88%",
|
||||
height: "80%",
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
border: "4px solid #000",
|
||||
background: "#fefbfb",
|
||||
borderRadius: "4px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
margin: "2rem",
|
||||
width: "88%",
|
||||
height: "80%",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
margin: "20px",
|
||||
width: "90%",
|
||||
height: "90%",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "90%",
|
||||
maxHeight: "90%",
|
||||
overflow: "hidden",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<p style={{ fontSize: 72, fontWeight: "bold" }}>{SITE.title}</p>
|
||||
<p style={{ fontSize: 28 }}>{SITE.desc}</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
width: "100%",
|
||||
marginBottom: "8px",
|
||||
fontSize: 28,
|
||||
}}
|
||||
>
|
||||
<span style={{ overflow: "hidden", fontWeight: "bold" }}>
|
||||
{new URL(SITE.website).hostname}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
embedFont: true,
|
||||
fonts: (await loadGoogleFonts(
|
||||
SITE.title + SITE.desc + SITE.website
|
||||
)) as FontOptions[],
|
||||
}
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user