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/components/BaseHead.astro | 86 +++++++++++++ src/components/FormattedDate.astro | 16 +++ src/components/Paginator.astro | 29 +++++ src/components/Search.astro | 162 +++++++++++++++++++++++++ src/components/SkipLink.astro | 3 + src/components/SocialList.astro | 42 +++++++ src/components/ThemeProvider.astro | 44 +++++++ src/components/ThemeToggle.astro | 90 ++++++++++++++ src/components/blog/Masthead.astro | 84 +++++++++++++ src/components/blog/PostPreview.astro | 24 ++++ src/components/blog/TOC.astro | 22 ++++ src/components/blog/TOCHeading.astro | 27 +++++ src/components/blog/webmentions/Comments.astro | 87 +++++++++++++ src/components/blog/webmentions/Likes.astro | 52 ++++++++ src/components/blog/webmentions/index.astro | 23 ++++ src/components/layout/Footer.astro | 27 +++++ src/components/layout/Header.astro | 118 ++++++++++++++++++ src/components/note/Note.astro | 48 ++++++++ 18 files changed, 984 insertions(+) create mode 100644 src/components/BaseHead.astro create mode 100644 src/components/FormattedDate.astro create mode 100644 src/components/Paginator.astro create mode 100644 src/components/Search.astro create mode 100644 src/components/SkipLink.astro create mode 100644 src/components/SocialList.astro create mode 100644 src/components/ThemeProvider.astro create mode 100644 src/components/ThemeToggle.astro create mode 100644 src/components/blog/Masthead.astro create mode 100644 src/components/blog/PostPreview.astro create mode 100644 src/components/blog/TOC.astro create mode 100644 src/components/blog/TOCHeading.astro create mode 100644 src/components/blog/webmentions/Comments.astro create mode 100644 src/components/blog/webmentions/Likes.astro create mode 100644 src/components/blog/webmentions/index.astro create mode 100644 src/components/layout/Footer.astro create mode 100644 src/components/layout/Header.astro create mode 100644 src/components/note/Note.astro (limited to 'src/components') diff --git a/src/components/BaseHead.astro b/src/components/BaseHead.astro new file mode 100644 index 0000000..7ff861f --- /dev/null +++ b/src/components/BaseHead.astro @@ -0,0 +1,86 @@ +--- +import { WEBMENTION_PINGBACK, WEBMENTION_URL } from "astro:env/client"; +import { siteConfig } from "@/site.config"; +import type { SiteMeta } from "@/types"; +import "@/styles/global.css"; + +type Props = SiteMeta; + +const { articleDate, description, ogImage, title } = Astro.props; + +const titleSeparator = "•"; +const siteTitle = `${title} ${titleSeparator} ${siteConfig.title}`; +const canonicalURL = new URL(Astro.url.pathname, Astro.site); +const socialImageURL = new URL(ogImage ? ogImage : "/social-card.png", Astro.url).href; +--- + + + +{siteTitle} + +{/* Icons */} + +{ + import.meta.env.PROD && ( + <> + {/* Favicon & Apple Icon */} + + + {/* Manifest */} + + + ) +} + +{/* Canonical URL */} + + +{/* Primary Meta Tags */} + + + + +{/* Open Graph / Facebook */} + + + + + + + + + +{ + articleDate && ( + <> + + + + ) +} + +{/* Twitter */} + + + + + + +{/* Sitemap */} + + +{/* RSS auto-discovery */} + + + +{/* Webmentions */} +{ + WEBMENTION_URL && ( + <> + + {WEBMENTION_PINGBACK && } + + ) +} + + diff --git a/src/components/FormattedDate.astro b/src/components/FormattedDate.astro new file mode 100644 index 0000000..aba7f4d --- /dev/null +++ b/src/components/FormattedDate.astro @@ -0,0 +1,16 @@ +--- +import { getFormattedDate } from "@/utils/date"; +import type { HTMLAttributes } from "astro/types"; + +type Props = HTMLAttributes<"time"> & { + date: Date; + dateTimeOptions?: Intl.DateTimeFormatOptions; +}; + +const { date, dateTimeOptions, ...attrs } = Astro.props; + +const postDate = getFormattedDate(date, dateTimeOptions); +const ISO = date.toISOString(); +--- + + diff --git a/src/components/Paginator.astro b/src/components/Paginator.astro new file mode 100644 index 0000000..0678487 --- /dev/null +++ b/src/components/Paginator.astro @@ -0,0 +1,29 @@ +--- +import type { PaginationLink } from "@/types"; + +interface Props { + nextUrl?: PaginationLink; + prevUrl?: PaginationLink; +} + +const { nextUrl, prevUrl } = Astro.props; +--- + +{ + (prevUrl || nextUrl) && ( + + ) +} diff --git a/src/components/Search.astro b/src/components/Search.astro new file mode 100644 index 0000000..7b98c1a --- /dev/null +++ b/src/components/Search.astro @@ -0,0 +1,162 @@ +--- +// Heavy inspiration taken from Astro Starlight -> https://github.com/withastro/starlight/blob/main/packages/starlight/components/Search.astro + +import "@/styles/blocks/search.css"; +--- + + + + +
+ + { + import.meta.env.DEV ? ( +
+

+ Search is only available in production builds.
+ Try building and previewing the site to test it out locally. +

+
+ ) : ( +
+ + ) + } +
+
+
+ + diff --git a/src/components/SkipLink.astro b/src/components/SkipLink.astro new file mode 100644 index 0000000..dae6bad --- /dev/null +++ b/src/components/SkipLink.astro @@ -0,0 +1,3 @@ +skip to content + diff --git a/src/components/SocialList.astro b/src/components/SocialList.astro new file mode 100644 index 0000000..00e7f97 --- /dev/null +++ b/src/components/SocialList.astro @@ -0,0 +1,42 @@ +--- +import { Icon } from "astro-icon/components"; + +/** + Uses https://www.astroicon.dev/getting-started/ + Find icons via guide: https://www.astroicon.dev/guides/customization/#open-source-icon-sets + Only installed pack is: @iconify-json/mdi +*/ +const socialLinks: { + friendlyName: string; + isWebmention?: boolean; + link: string; + name: string; +}[] = [ + { + friendlyName: "Github", + link: "https://github.com/chrismwilliams/astro-cactus", + name: "mdi:github", + }, +]; +--- + +
+

Find me on

+ +
diff --git a/src/components/ThemeProvider.astro b/src/components/ThemeProvider.astro new file mode 100644 index 0000000..5f0723d --- /dev/null +++ b/src/components/ThemeProvider.astro @@ -0,0 +1,44 @@ +{/* Inlined to avoid FOUC. This is a parser blocking script. */} + diff --git a/src/components/ThemeToggle.astro b/src/components/ThemeToggle.astro new file mode 100644 index 0000000..7621daf --- /dev/null +++ b/src/components/ThemeToggle.astro @@ -0,0 +1,90 @@ + + + + + diff --git a/src/components/blog/Masthead.astro b/src/components/blog/Masthead.astro new file mode 100644 index 0000000..1f52383 --- /dev/null +++ b/src/components/blog/Masthead.astro @@ -0,0 +1,84 @@ +--- +import { Image } from "astro:assets"; +import type { CollectionEntry } from "astro:content"; +import FormattedDate from "@/components/FormattedDate.astro"; + +interface Props { + content: CollectionEntry<"post">; + readingTime: string; +} + +const { + content: { data }, + readingTime, +} = Astro.props; + +const dateTimeOptions: Intl.DateTimeFormatOptions = { + month: "long", +}; +--- + +{ + data.coverImage && ( +
+ {data.coverImage.alt} +
+ ) +} +{data.draft ? (Draft) : null} +

+ {data.title} +

+
+

+ /{" "} + {readingTime} +

+ { + data.updatedDate && ( + + Updated: + + + ) + } +
+{ + !!data.tags?.length && ( +
+ + {data.tags.map((tag, i) => ( + <> + {/* prettier-ignore */} + + View more blogs with the tag {tag} + {i < data.tags.length - 1 && ", "} + + + ))} +
+ ) +} diff --git a/src/components/blog/PostPreview.astro b/src/components/blog/PostPreview.astro new file mode 100644 index 0000000..fc1a9a3 --- /dev/null +++ b/src/components/blog/PostPreview.astro @@ -0,0 +1,24 @@ +--- +import type { CollectionEntry } from "astro:content"; +import FormattedDate from "@/components/FormattedDate.astro"; +import type { HTMLTag, Polymorphic } from "astro/types"; + +type Props = Polymorphic<{ as: Tag }> & { + post: CollectionEntry<"post">; + withDesc?: boolean; +}; + +const { as: Tag = "div", post, withDesc = false } = Astro.props; +--- + + + + {post.data.draft && (Draft) } + + {post.data.title} + + +{withDesc && {post.data.description}} diff --git a/src/components/blog/TOC.astro b/src/components/blog/TOC.astro new file mode 100644 index 0000000..6649546 --- /dev/null +++ b/src/components/blog/TOC.astro @@ -0,0 +1,22 @@ +--- +import { generateToc } from "@/utils/generateToc"; +import type { MarkdownHeading } from "astro"; +import TOCHeading from "./TOCHeading.astro"; + +interface Props { + headings: MarkdownHeading[]; +} + +const { headings } = Astro.props; + +const toc = generateToc(headings); +--- + +
+ Table of Contents + +
diff --git a/src/components/blog/TOCHeading.astro b/src/components/blog/TOCHeading.astro new file mode 100644 index 0000000..b9dd486 --- /dev/null +++ b/src/components/blog/TOCHeading.astro @@ -0,0 +1,27 @@ +--- +import type { TocItem } from "@/utils/generateToc"; + +interface Props { + heading: TocItem; +} + +const { + heading: { children, depth, slug, text }, +} = Astro.props; +--- + +
  • 2 ? "ms-2" : ""}`}> + {text} + { + !!children.length && ( +
      + {children.map((subheading) => ( + + ))} +
    + ) + } +
  • diff --git a/src/components/blog/webmentions/Comments.astro b/src/components/blog/webmentions/Comments.astro new file mode 100644 index 0000000..5177d57 --- /dev/null +++ b/src/components/blog/webmentions/Comments.astro @@ -0,0 +1,87 @@ +--- +import { Image } from "astro:assets"; +import type { WebmentionsChildren } from "@/types"; +import { Icon } from "astro-icon/components"; + +interface Props { + mentions: WebmentionsChildren[]; +} + +const { mentions } = Astro.props; + +const validComments = ["mention-of", "in-reply-to"]; + +const comments = mentions.filter( + (mention) => validComments.includes(mention["wm-property"]) && mention.content?.text, +); +--- + +{ + !!comments.length && ( +
    +

    + {comments.length} Mention{comments.length > 1 ? "s" : ""} +

    +
      + {comments.map((mention) => ( +
    • + {mention.author?.photo && mention.author.photo !== "" ? ( + mention.author.url && mention.author.url !== "" ? ( + + {mention.author?.name} + + ) : ( + {mention.author?.name} + ) + ) : null} +
      +
      +

      + {mention.author?.name} +

      + + + +
      +

      + {mention.content?.text} +

      +
      +
    • + ))} +
    +
    + ) +} diff --git a/src/components/blog/webmentions/Likes.astro b/src/components/blog/webmentions/Likes.astro new file mode 100644 index 0000000..7862c43 --- /dev/null +++ b/src/components/blog/webmentions/Likes.astro @@ -0,0 +1,52 @@ +--- +import { Image } from "astro:assets"; +import type { WebmentionsChildren } from "@/types"; + +interface Props { + mentions: WebmentionsChildren[]; +} + +const { mentions } = Astro.props; +const MAX_LIKES = 10; + +const likes = mentions.filter((mention) => mention["wm-property"] === "like-of"); +const likesToShow = likes + .filter((like) => like.author?.photo && like.author.photo !== "") + .slice(0, MAX_LIKES); +--- + +{ + !!likes.length && ( +
    +

    + {likes.length} + {likes.length > 1 ? " People" : " Person"} liked this +

    + {!!likesToShow.length && ( +
      + {likesToShow.map((like) => ( +
    • + + + {like.author!.name} + + +
    • + ))} +
    + )} +
    + ) +} diff --git a/src/components/blog/webmentions/index.astro b/src/components/blog/webmentions/index.astro new file mode 100644 index 0000000..232b4f3 --- /dev/null +++ b/src/components/blog/webmentions/index.astro @@ -0,0 +1,23 @@ +--- +import { getWebmentionsForUrl } from "@/utils/webmentions"; +import Comments from "./Comments.astro"; +import Likes from "./Likes.astro"; + +const url = new URL(Astro.url.pathname, Astro.site); + +const webMentions = await getWebmentionsForUrl(`${url}`); + +// Return if no webmentions +if (!webMentions.length) return; +--- + +
    +

    Webmentions for this post

    +
    + + +
    +

    + Responses powered by{" "} + Webmentions +

    diff --git a/src/components/layout/Footer.astro b/src/components/layout/Footer.astro new file mode 100644 index 0000000..5eea6e8 --- /dev/null +++ b/src/components/layout/Footer.astro @@ -0,0 +1,27 @@ +--- +import { menuLinks, siteConfig } from "@/site.config"; + +const year = new Date().getFullYear(); +--- + +
    +
    + © {siteConfig.author} + {year}. 🚀 {siteConfig.title} +
    + +
    diff --git a/src/components/layout/Header.astro b/src/components/layout/Header.astro new file mode 100644 index 0000000..65ea5cc --- /dev/null +++ b/src/components/layout/Header.astro @@ -0,0 +1,118 @@ +--- +import Search from "@/components/Search.astro"; +import ThemeToggle from "@/components/ThemeToggle.astro"; +import { menuLinks } from "@/site.config"; +import {siteConfig} from "../../site.config"; +--- + +
    +
    + + + {siteConfig.title} + + +
    + + + + + +
    + + diff --git a/src/components/note/Note.astro b/src/components/note/Note.astro new file mode 100644 index 0000000..cff3414 --- /dev/null +++ b/src/components/note/Note.astro @@ -0,0 +1,48 @@ +--- +import { type CollectionEntry, render } from "astro:content"; +import FormattedDate from "@/components/FormattedDate.astro"; +import type { HTMLTag, Polymorphic } from "astro/types"; + +type Props = Polymorphic<{ as: Tag }> & { + note: CollectionEntry<"note">; + isPreview?: boolean | undefined; +}; + +const { as: Tag = "div", note, isPreview = false } = Astro.props; +const { Content } = await render(note); +--- + + -- cgit v1.2.3