From 456cf011b36de91c9936994b1fa45703adcd309b Mon Sep 17 00:00:00 2001 From: Dawid Rycerz Date: Thu, 3 Jul 2025 10:56:21 +0300 Subject: Initial fork of chrismwilliams/astro-theme-cactus theme --- src/pages/404.astro | 13 ++++ src/pages/about.astro | 36 ++++++++++ src/pages/index.astro | 61 +++++++++++++++++ src/pages/notes/[...page].astro | 63 ++++++++++++++++++ src/pages/notes/[...slug].astro | 31 +++++++++ src/pages/notes/rss.xml.ts | 18 +++++ src/pages/og-image/[...slug].png.ts | 90 +++++++++++++++++++++++++ src/pages/posts/[...page].astro | 125 +++++++++++++++++++++++++++++++++++ src/pages/posts/[...slug].astro | 24 +++++++ src/pages/rss.xml.ts | 19 ++++++ src/pages/tags/[tag]/[...page].astro | 79 ++++++++++++++++++++++ src/pages/tags/index.astro | 35 ++++++++++ 12 files changed, 594 insertions(+) create mode 100644 src/pages/404.astro create mode 100644 src/pages/about.astro create mode 100644 src/pages/index.astro create mode 100644 src/pages/notes/[...page].astro create mode 100644 src/pages/notes/[...slug].astro create mode 100644 src/pages/notes/rss.xml.ts create mode 100644 src/pages/og-image/[...slug].png.ts create mode 100644 src/pages/posts/[...page].astro create mode 100644 src/pages/posts/[...slug].astro create mode 100644 src/pages/rss.xml.ts create mode 100644 src/pages/tags/[tag]/[...page].astro create mode 100644 src/pages/tags/index.astro (limited to 'src/pages') diff --git a/src/pages/404.astro b/src/pages/404.astro new file mode 100644 index 0000000..d02cca3 --- /dev/null +++ b/src/pages/404.astro @@ -0,0 +1,13 @@ +--- +import PageLayout from "@/layouts/Base.astro"; + +const meta = { + description: "Oops! It looks like this page is lost in space!", + title: "Oops! You found a missing page!", +}; +--- + + +

404 | Oops something went wrong

+

Please use the navigation to find your way back

+
diff --git a/src/pages/about.astro b/src/pages/about.astro new file mode 100644 index 0000000..190bbd3 --- /dev/null +++ b/src/pages/about.astro @@ -0,0 +1,36 @@ +--- +import PageLayout from "@/layouts/Base.astro"; + +const meta = { + description: "I'm a starter theme for Astro.build", + title: "About", +}; +--- + + +

About

+
+

+ Hi, I’m a starter Astro. I’m particularly great for getting you started with your own blogging + website. +

+

Here are my some of my awesome built in features:

+
    +
  • I'm ultra fast as I'm a static site
  • +
  • I'm fully responsive
  • +
  • I come with a light and dark mode
  • +
  • I'm easy to customise and add additional content
  • +
  • I have Tailwind CSS styling
  • +
  • Shiki code syntax highlighting
  • +
  • Satori for auto generating OG images for blog posts
  • +
+

+ Clone or fork my repo if you like me! +

+
+
diff --git a/src/pages/index.astro b/src/pages/index.astro new file mode 100644 index 0000000..d953797 --- /dev/null +++ b/src/pages/index.astro @@ -0,0 +1,61 @@ +--- +import { type CollectionEntry, getCollection } from "astro:content"; +import SocialList from "@/components/SocialList.astro"; +import PostPreview from "@/components/blog/PostPreview.astro"; +import Note from "@/components/note/Note.astro"; +import { getAllPosts } from "@/data/post"; +import PageLayout from "@/layouts/Base.astro"; +import { collectionDateSort } from "@/utils/date"; + +// Posts +const MAX_POSTS = 10; +const allPosts = await getAllPosts(); +const allPostsByDate = allPosts + .sort(collectionDateSort) + .slice(0, MAX_POSTS) as CollectionEntry<"post">[]; + +// Notes +const MAX_NOTES = 5; +const allNotes = await getCollection("note"); +const latestNotes = allNotes.sort(collectionDateSort).slice(0, MAX_NOTES); +--- + + +
+

Hello World!

+

+ Hi, I’m a theme for Astro, a simple starter that you can use to create your website or blog. + If you want to know more about how you can customise me, add more posts, and make it your own, + click on the GitHub icon link below and it will take you to my repo. +

+ +
+
+

Posts

+
    + { + allPostsByDate.map((p) => ( +
  • + +
  • + )) + } +
+
+ { + latestNotes.length > 0 && ( +
+

+ Notes +

+
    + {latestNotes.map((note) => ( +
  • + +
  • + ))} +
+
+ ) + } +
diff --git a/src/pages/notes/[...page].astro b/src/pages/notes/[...page].astro new file mode 100644 index 0000000..fdc5af9 --- /dev/null +++ b/src/pages/notes/[...page].astro @@ -0,0 +1,63 @@ +--- +import { type CollectionEntry, getCollection } from "astro:content"; +import Pagination from "@/components/Paginator.astro"; +import Note from "@/components/note/Note.astro"; +import PageLayout from "@/layouts/Base.astro"; +import { collectionDateSort } from "@/utils/date"; +import type { GetStaticPaths, Page } from "astro"; +import { Icon } from "astro-icon/components"; + +export const getStaticPaths = (async ({ paginate }) => { + const MAX_NOTES_PER_PAGE = 10; + const allNotes = await getCollection("note"); + return paginate(allNotes.sort(collectionDateSort), { pageSize: MAX_NOTES_PER_PAGE }); +}) satisfies GetStaticPaths; + +interface Props { + page: Page>; + uniqueTags: string[]; +} + +const { page } = Astro.props; + +const meta = { + description: "Read my collection of notes", + title: "Notes", +}; + +const paginationProps = { + ...(page.url.prev && { + prevUrl: { + text: "← Previous Page", + url: page.url.prev, + }, + }), + ...(page.url.next && { + nextUrl: { + text: "Next Page →", + url: page.url.next, + }, + }), +}; +--- + + +
+

+ Notes + RSS feed + +

+
    + { + page.data.map((note) => ( +
  • + +
  • + )) + } +
+ +
+
diff --git a/src/pages/notes/[...slug].astro b/src/pages/notes/[...slug].astro new file mode 100644 index 0000000..2ce847d --- /dev/null +++ b/src/pages/notes/[...slug].astro @@ -0,0 +1,31 @@ +--- +import { getCollection } from "astro:content"; + +import Note from "@/components/note/Note.astro"; +import PageLayout from "@/layouts/Base.astro"; +import type { GetStaticPaths, InferGetStaticPropsType } from "astro"; + +// if you're using an adaptor in SSR mode, getStaticPaths wont work -> https://docs.astro.build/en/guides/routing/#modifying-the-slug-example-for-ssr +export const getStaticPaths = (async () => { + const allNotes = await getCollection("note"); + return allNotes.map((note) => ({ + params: { slug: note.id }, + props: { note }, + })); +}) satisfies GetStaticPaths; + +export type Props = InferGetStaticPropsType; + +const { note } = Astro.props; + +const meta = { + description: + note.data.description || + `Read about my note posted on: ${note.data.publishDate.toLocaleDateString()}`, + title: note.data.title, +}; +--- + + + + diff --git a/src/pages/notes/rss.xml.ts b/src/pages/notes/rss.xml.ts new file mode 100644 index 0000000..0f1f945 --- /dev/null +++ b/src/pages/notes/rss.xml.ts @@ -0,0 +1,18 @@ +import { getCollection } from "astro:content"; +import { siteConfig } from "@/site.config"; +import rss from "@astrojs/rss"; + +export const GET = async () => { + const notes = await getCollection("note"); + + return rss({ + title: siteConfig.title, + description: siteConfig.description, + site: import.meta.env.SITE, + items: notes.map((note) => ({ + title: note.data.title, + pubDate: note.data.publishDate, + link: `notes/${note.id}/`, + })), + }); +}; diff --git a/src/pages/og-image/[...slug].png.ts b/src/pages/og-image/[...slug].png.ts new file mode 100644 index 0000000..a4982d8 --- /dev/null +++ b/src/pages/og-image/[...slug].png.ts @@ -0,0 +1,90 @@ +import RobotoMonoBold from "@/assets/roboto-mono-700.ttf"; +import RobotoMono from "@/assets/roboto-mono-regular.ttf"; +import { getAllPosts } from "@/data/post"; +import { siteConfig } from "@/site.config"; +import { getFormattedDate } from "@/utils/date"; +import { Resvg } from "@resvg/resvg-js"; +import type { APIContext, InferGetStaticPropsType } from "astro"; +import satori, { type SatoriOptions } from "satori"; +import { html } from "satori-html"; + +const ogOptions: SatoriOptions = { + // debug: true, + fonts: [ + { + data: Buffer.from(RobotoMono), + name: "Roboto Mono", + style: "normal", + weight: 400, + }, + { + data: Buffer.from(RobotoMonoBold), + name: "Roboto Mono", + style: "normal", + weight: 700, + }, + ], + height: 630, + width: 1200, +}; + +const markup = (title: string, pubDate: string) => + html`
+
+

${pubDate}

+

${title}

+
+
+
+ + + + + + +

${siteConfig.title}

+
+

by ${siteConfig.author}

+
+
`; + +type Props = InferGetStaticPropsType; + +export async function GET(context: APIContext) { + const { pubDate, title } = context.props as Props; + + const postDate = getFormattedDate(pubDate, { + month: "long", + weekday: "long", + }); + const svg = await satori(markup(title, postDate), ogOptions); + const png = new Resvg(svg).render().asPng(); + return new Response(png, { + headers: { + "Cache-Control": "public, max-age=31536000, immutable", + "Content-Type": "image/png", + }, + }); +} + +export async function getStaticPaths() { + const posts = await getAllPosts(); + return posts + .filter(({ data }) => !data.ogImage) + .map((post) => ({ + params: { slug: post.id }, + props: { + pubDate: post.data.updatedDate ?? post.data.publishDate, + title: post.data.title, + }, + })); +} diff --git a/src/pages/posts/[...page].astro b/src/pages/posts/[...page].astro new file mode 100644 index 0000000..495fc7b --- /dev/null +++ b/src/pages/posts/[...page].astro @@ -0,0 +1,125 @@ +--- +import type { CollectionEntry } from "astro:content"; +import Pagination from "@/components/Paginator.astro"; +import PostPreview from "@/components/blog/PostPreview.astro"; +import { getAllPosts, getUniqueTags, groupPostsByYear } from "@/data/post"; +import PageLayout from "@/layouts/Base.astro"; +import { collectionDateSort } from "@/utils/date"; +import type { GetStaticPaths, Page } from "astro"; +import { Icon } from "astro-icon/components"; + +export const getStaticPaths = (async ({ paginate }) => { + const MAX_POSTS_PER_PAGE = 10; + const MAX_TAGS = 7; + const allPosts = await getAllPosts(); + const uniqueTags = getUniqueTags(allPosts).slice(0, MAX_TAGS); + return paginate(allPosts.sort(collectionDateSort), { + pageSize: MAX_POSTS_PER_PAGE, + props: { uniqueTags }, + }); +}) satisfies GetStaticPaths; + +interface Props { + page: Page>; + uniqueTags: string[]; +} + +const { page, uniqueTags } = Astro.props; + +const meta = { + description: "Read my collection of posts and the things that interest me", + title: "Posts", +}; + +const paginationProps = { + ...(page.url.prev && { + prevUrl: { + text: "← Previous Page", + url: page.url.prev, + }, + }), + ...(page.url.next && { + nextUrl: { + text: "Next Page →", + url: page.url.next, + }, + }), +}; + +const groupedByYear = groupPostsByYear(page.data); +const descYearKeys = Object.keys(groupedByYear).sort((a, b) => +b - +a); +--- + + +
+

Posts

+ + RSS feed + +
+
+
+ { + descYearKeys.map((yearKey) => ( +
+

+ Posts in + {yearKey} +

+
    + {groupedByYear[yearKey]?.map((p) => ( +
  • + +
  • + ))} +
+
+ )) + } + +
+ { + !!uniqueTags.length && ( + + ) + } +
+
diff --git a/src/pages/posts/[...slug].astro b/src/pages/posts/[...slug].astro new file mode 100644 index 0000000..ca9c491 --- /dev/null +++ b/src/pages/posts/[...slug].astro @@ -0,0 +1,24 @@ +--- +import { render } from "astro:content"; +import { getAllPosts } from "@/data/post"; +import PostLayout from "@/layouts/BlogPost.astro"; +import type { GetStaticPaths, InferGetStaticPropsType } from "astro"; + +// if you're using an adaptor in SSR mode, getStaticPaths wont work -> https://docs.astro.build/en/guides/routing/#modifying-the-slug-example-for-ssr +export const getStaticPaths = (async () => { + const blogEntries = await getAllPosts(); + return blogEntries.map((post) => ({ + params: { slug: post.id }, + props: { post }, + })); +}) satisfies GetStaticPaths; + +type Props = InferGetStaticPropsType; + +const { post } = Astro.props; +const { Content } = await render(post); +--- + + + + diff --git a/src/pages/rss.xml.ts b/src/pages/rss.xml.ts new file mode 100644 index 0000000..1c305af --- /dev/null +++ b/src/pages/rss.xml.ts @@ -0,0 +1,19 @@ +import { getAllPosts } from "@/data/post"; +import { siteConfig } from "@/site.config"; +import rss from "@astrojs/rss"; + +export const GET = async () => { + const posts = await getAllPosts(); + + return rss({ + title: siteConfig.title, + description: siteConfig.description, + site: import.meta.env.SITE, + items: posts.map((post) => ({ + title: post.data.title, + description: post.data.description, + pubDate: post.data.publishDate, + link: `posts/${post.id}/`, + })), + }); +}; diff --git a/src/pages/tags/[tag]/[...page].astro b/src/pages/tags/[tag]/[...page].astro new file mode 100644 index 0000000..56923fb --- /dev/null +++ b/src/pages/tags/[tag]/[...page].astro @@ -0,0 +1,79 @@ +--- +import { render } from "astro:content"; +import Pagination from "@/components/Paginator.astro"; +import PostPreview from "@/components/blog/PostPreview.astro"; +import { getAllPosts, getTagMeta, getUniqueTags } from "@/data/post"; +import PageLayout from "@/layouts/Base.astro"; +import { collectionDateSort } from "@/utils/date"; +import type { GetStaticPaths, InferGetStaticPropsType } from "astro"; +import { Icon } from "astro-icon/components"; + +export const getStaticPaths = (async ({ paginate }) => { + const allPosts = await getAllPosts(); + const sortedPosts = allPosts.sort(collectionDateSort); + const uniqueTags = getUniqueTags(sortedPosts); + + return uniqueTags.flatMap((tag) => { + const postsWithTag = sortedPosts.filter((post) => post.data.tags.includes(tag)); + return paginate(postsWithTag, { + pageSize: 10, + params: { tag }, + }); + }); +}) satisfies GetStaticPaths; + +type Props = InferGetStaticPropsType; + +const { page } = Astro.props as Props; +const { tag } = Astro.params; +const tagMeta = await getTagMeta(tag); + +const TagContent = tagMeta ? (await render(tagMeta)).Content : null; + +const meta = { + description: tagMeta?.data.description ?? `View all posts with the tag - ${tag}`, + title: tagMeta?.data.title ?? `Posts about ${tag}`, +}; + +const paginationProps = { + ...(page.url.prev && { + prevUrl: { + text: "← Previous Tags", + url: page.url.prev, + }, + }), + ...(page.url.next && { + nextUrl: { + text: "Next Tags →", + url: page.url.next, + }, + }), +}; +--- + + + +

{tagMeta?.data.title ?? `Posts about ${tag}`}

+
+ {tagMeta?.data.description &&

{tagMeta.data.description}

} + {TagContent && } +
+
    + { + page.data.map((p) => ( +
  • + +
  • + )) + } +
+ +
diff --git a/src/pages/tags/index.astro b/src/pages/tags/index.astro new file mode 100644 index 0000000..df1f630 --- /dev/null +++ b/src/pages/tags/index.astro @@ -0,0 +1,35 @@ +--- +import { getAllPosts, getUniqueTagsWithCount } from "@/data/post"; +import PageLayout from "@/layouts/Base.astro"; + +const allPosts = await getAllPosts(); +const allTags = getUniqueTagsWithCount(allPosts); + +const meta = { + description: "A list of all the topics I've written about in my posts", + title: "All Tags", +}; +--- + + +

Tags

+
    + { + allTags.map(([tag, val]) => ( +
  • + + #{tag} + + + - {val} Post{val > 1 && "s"} + +
  • + )) + } +
+
-- cgit v1.2.3