summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--astro.config.ts15
-rw-r--r--package.json1
-rw-r--r--pnpm-lock.yaml8
-rw-r--r--src/components/BaseHead.astro11
-rw-r--r--src/components/blog/Masthead.astro7
-rw-r--r--src/components/blog/webmentions/Comments.astro90
-rw-r--r--src/components/blog/webmentions/Likes.astro54
-rw-r--r--src/components/blog/webmentions/index.astro29
-rw-r--r--src/i18n/translations.ts14
-rw-r--r--src/layouts/BlogPost.astro8
-rw-r--r--src/plugins/remark-reading-time.ts11
-rw-r--r--src/types.ts56
-rw-r--r--src/utils/webmentions.ts115
13 files changed, 5 insertions, 414 deletions
diff --git a/astro.config.ts b/astro.config.ts
index cf262a0..792a5f2 100644
--- a/astro.config.ts
+++ b/astro.config.ts
@@ -4,7 +4,7 @@ import { rehypeHeadingIds } from "@astrojs/markdown-remark";
import mdx from "@astrojs/mdx";
import sitemap from "@astrojs/sitemap";
import tailwind from "@tailwindcss/vite";
-import { defineConfig, envField } from "astro/config";
+import { defineConfig } from "astro/config";
import expressiveCode from "astro-expressive-code";
import icon from "astro-icon";
import robotsTxt from "astro-robots-txt";
@@ -16,15 +16,11 @@ import rehypeUnwrapImages from "rehype-unwrap-images";
import remarkDirective from "remark-directive"; /* Handle ::: directives as nodes */
import robotsConfig from "./robots-txt.config";
import { remarkAdmonitions } from "./src/plugins/remark-admonitions"; /* Add admonitions */
-import { remarkReadingTime } from "./src/plugins/remark-reading-time";
import { expressiveCodeOptions, siteConfig } from "./src/site.config";
// https://astro.build/config
export default defineConfig({
site: siteConfig.url,
- image: {
- domains: ["webmention.io"],
- },
integrations: [
expressiveCode(expressiveCodeOptions),
icon(),
@@ -79,7 +75,7 @@ export default defineConfig({
],
rehypeUnwrapImages,
],
- remarkPlugins: [remarkReadingTime, remarkDirective, remarkAdmonitions],
+ remarkPlugins: [remarkDirective, remarkAdmonitions],
remarkRehype: {
footnoteLabelProperties: {
className: [""],
@@ -94,13 +90,6 @@ export default defineConfig({
},
plugins: [tailwind(), rawFonts([".ttf", ".woff"])],
},
- env: {
- schema: {
- WEBMENTION_API_KEY: envField.string({ context: "server", access: "secret", optional: true }),
- WEBMENTION_URL: envField.string({ context: "client", access: "public", optional: true }),
- WEBMENTION_PINGBACK: envField.string({ context: "client", access: "public", optional: true }),
- },
- },
});
function rawFonts(ext: string[]) {
diff --git a/package.json b/package.json
index 09ccde9..b1fdb22 100644
--- a/package.json
+++ b/package.json
@@ -64,7 +64,6 @@
"prettier": "^3.6.2",
"prettier-plugin-astro": "0.14.1",
"prettier-plugin-tailwindcss": "^0.6.13",
- "reading-time": "^1.5.0",
"tailwindcss": "4.1.11",
"typescript": "^5.8.3",
"wrangler": "^4.59.0"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 395f4ea..2e47431 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -144,9 +144,6 @@ importers:
prettier-plugin-tailwindcss:
specifier: ^0.6.13
version: 0.6.13(prettier-plugin-astro@0.14.1)(prettier@3.6.2)
- reading-time:
- specifier: ^1.5.0
- version: 1.5.0
tailwindcss:
specifier: 4.1.11
version: 4.1.11
@@ -2984,9 +2981,6 @@ packages:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
- reading-time@1.5.0:
- resolution: {integrity: sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==}
-
recma-build-jsx@1.0.0:
resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==}
@@ -6869,8 +6863,6 @@ snapshots:
readdirp@4.1.2: {}
- reading-time@1.5.0: {}
-
recma-build-jsx@1.0.0:
dependencies:
'@types/estree': 1.0.7
diff --git a/src/components/BaseHead.astro b/src/components/BaseHead.astro
index 14286c3..2e5a9cd 100644
--- a/src/components/BaseHead.astro
+++ b/src/components/BaseHead.astro
@@ -1,5 +1,4 @@
---
-import { WEBMENTION_PINGBACK, WEBMENTION_URL } from "astro:env/client";
import { siteConfig } from "@/site.config";
import type { SiteMeta } from "@/types";
import "@/styles/global.css";
@@ -96,16 +95,6 @@ const ogLocale = getOgLocale(lang);
{/* RSS auto-discovery */}
<link href="/rss.xml" title="Blog" rel="alternate" type="application/rss+xml" />
-{/* Webmentions */}
-{
- WEBMENTION_URL && (
- <>
- <link href={WEBMENTION_URL} rel="webmention" />
- {WEBMENTION_PINGBACK && <link href={WEBMENTION_PINGBACK} rel="pingback" />}
- </>
- )
-}
-
{/* Plausible Analytics */}
<link rel="dns-prefetch" href="//analytics.craftknight.com" />
<script
diff --git a/src/components/blog/Masthead.astro b/src/components/blog/Masthead.astro
index e7b3ea9..7a50d8a 100644
--- a/src/components/blog/Masthead.astro
+++ b/src/components/blog/Masthead.astro
@@ -6,13 +6,11 @@ import { t } from "@/i18n/translations";
interface Props {
content: CollectionEntry<"post">;
- readingTime: string;
language?: string | undefined;
}
const {
content: { data },
- readingTime,
language,
} = Astro.props;
@@ -41,10 +39,7 @@ const dateTimeOptions: Intl.DateTimeFormatOptions = {
</h1>
<div class="flex flex-wrap items-center gap-x-3 gap-y-2">
<p class="font-semibold">
- <FormattedDate date={data.publishDate} dateTimeOptions={dateTimeOptions} locale={language} /> /{
- " "
- }
- {readingTime}
+ <FormattedDate date={data.publishDate} dateTimeOptions={dateTimeOptions} locale={language} />
</p>
{
data.updatedDate && (
diff --git a/src/components/blog/webmentions/Comments.astro b/src/components/blog/webmentions/Comments.astro
deleted file mode 100644
index 9d274f9..0000000
--- a/src/components/blog/webmentions/Comments.astro
+++ /dev/null
@@ -1,90 +0,0 @@
----
-import { Image } from "astro:assets";
-import { Icon } from "astro-icon/components";
-import { t } from "@/i18n/translations";
-import type { WebmentionsChildren } from "@/types";
-
-interface Props {
- mentions: WebmentionsChildren[];
- language?: string | undefined;
-}
-
-const { mentions, language } = Astro.props;
-
-const validComments = ["mention-of", "in-reply-to"];
-
-const comments = mentions.filter(
- (mention) => validComments.includes(mention["wm-property"]) && mention.content?.text,
-);
----
-
-{
- !!comments.length && (
- <div>
- <p class="text-accent-2 mb-0">
- <strong>{comments.length}</strong>{" "}
- {comments.length > 1 ? t(language, "mentions") : t(language, "mention")}
- </p>
- <ul class="divide-global-text/20 mt-0 divide-y ps-0" role="list">
- {comments.map((mention) => (
- <li class="p-comment h-cite my-0 flex items-start gap-x-5 py-5">
- {mention.author?.photo && mention.author.photo !== "" ? (
- mention.author.url && mention.author.url !== "" ? (
- <a
- class="u-author not-prose ring-global-text hover:ring-link focus-visible:ring-link shrink-0 overflow-hidden rounded-full ring-2 hover:ring-4 focus-visible:ring-4"
- href={mention.author.url}
- rel="noreferrer"
- target="_blank"
- title={mention.author.name}
- >
- <Image
- alt={mention.author?.name}
- class="u-photo my-0 h-12 w-12"
- height={48}
- src={mention.author?.photo}
- width={48}
- />
- </a>
- ) : (
- <Image
- alt={mention.author?.name}
- class="u-photo my-0 h-12 w-12 rounded-full"
- height={48}
- src={mention.author?.photo}
- width={48}
- />
- )
- ) : null}
- <div class="flex-auto">
- <div class="p-author h-card flex items-center justify-between gap-x-2">
- <p class="p-name text-accent-2 my-0 line-clamp-1 font-semibold">
- {mention.author?.name}
- </p>
- <a
- aria-labelledby="cmt-source"
- class="u-url not-prose hover:text-link"
- href={mention.url}
- rel="noreferrer"
- target="_blank"
- >
- <span class="hidden" id="cmt-source">
- {t(language, "visitWebmentionSource")}
- </span>
- <Icon
- aria-hidden="true"
- class="h-5 w-5"
- focusable="false"
- name="mdi:open-in-new"
- />
- </a>
- </div>
- <p class="comment-content mt-1 mb-0 break-words [word-break:break-word]">
- {mention.content?.text}
- </p>
- </div>
- </li>
- ))}
- </ul>
- </div>
- )
-}
diff --git a/src/components/blog/webmentions/Likes.astro b/src/components/blog/webmentions/Likes.astro
deleted file mode 100644
index fce1a96..0000000
--- a/src/components/blog/webmentions/Likes.astro
+++ /dev/null
@@ -1,54 +0,0 @@
----
-import { Image } from "astro:assets";
-import { t } from "@/i18n/translations";
-import type { WebmentionsChildren } from "@/types";
-
-interface Props {
- mentions: WebmentionsChildren[];
- language?: string | undefined;
-}
-
-const { mentions, language } = 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 && (
- <div>
- <p class="text-accent-2 mb-0">
- <strong>{likes.length}</strong>{" "}
- {likes.length > 1 ? t(language, "peopleLiked") : t(language, "personLiked")}
- </p>
- {!!likesToShow.length && (
- <ul class="flex list-none flex-wrap overflow-hidden ps-2" role="list">
- {likesToShow.map((like) => (
- <li class="p-like h-cite -ms-2">
- <a
- class="u-url not-prose ring-global-text hover:ring-link focus-visible:ring-link relative inline-block overflow-hidden rounded-full ring-2 hover:z-10 hover:ring-4 focus-visible:z-10 focus-visible:ring-4"
- href={like.author?.url}
- rel="noreferrer"
- target="_blank"
- title={like.author?.name}
- >
- <span class="p-author h-card">
- <Image
- alt={like.author!.name}
- class="u-photo my-0 inline-block h-12 w-12"
- height={48}
- src={like.author!.photo}
- width={48}
- />
- </span>
- </a>
- </li>
- ))}
- </ul>
- )}
- </div>
- )
-}
diff --git a/src/components/blog/webmentions/index.astro b/src/components/blog/webmentions/index.astro
deleted file mode 100644
index 80376f0..0000000
--- a/src/components/blog/webmentions/index.astro
+++ /dev/null
@@ -1,29 +0,0 @@
----
-import { t } from "@/i18n/translations";
-import { getWebmentionsForUrl } from "@/utils/webmentions";
-import Comments from "./Comments.astro";
-import Likes from "./Likes.astro";
-
-interface Props {
- language?: string | undefined;
-}
-
-const { language } = Astro.props;
-const url = new URL(Astro.url.pathname, Astro.site);
-
-const webMentions = await getWebmentionsForUrl(`${url}`);
-
-// Return if no webmentions
-if (!webMentions.length) return;
----
-
-<hr class="border-solid" />
-<h2 class="mb-8 before:hidden">{t(language, "webmentionsTitle")}</h2>
-<div class="space-y-10">
- <Likes mentions={webMentions} language={language} />
- <Comments mentions={webMentions} language={language} />
-</div>
-<p class="mt-8">
- {t(language, "responsesPoweredBy")}{" "}
- <a href="https://webmention.io" rel="noreferrer" target="_blank">Webmentions</a>
-</p>
diff --git a/src/i18n/translations.ts b/src/i18n/translations.ts
index 7631ad7..b68893a 100644
--- a/src/i18n/translations.ts
+++ b/src/i18n/translations.ts
@@ -4,26 +4,12 @@ export const translations = {
backToTop: "Back to top",
updated: "Updated:",
viewMoreWithTag: "View more blogs with the tag",
- webmentionsTitle: "Webmentions for this post",
- responsesPoweredBy: "Responses powered by",
- mention: "Mention",
- mentions: "Mentions",
- visitWebmentionSource: "Visit the source of this webmention",
- personLiked: "Person liked this",
- peopleLiked: "People liked this",
},
pl: {
viewOriginalPost: "Zobacz oryginalny wpis na Pleroma →",
backToTop: "Powrót na górę",
updated: "Zaktualizowano:",
viewMoreWithTag: "Zobacz więcej wpisów z tagiem",
- webmentionsTitle: "Webmentions dla tego wpisu",
- responsesPoweredBy: "Odpowiedzi dzięki",
- mention: "Wzmianka",
- mentions: "Wzmianki",
- visitWebmentionSource: "Odwiedź źródło tej wzmianki",
- personLiked: "osoba polubiła",
- peopleLiked: "osób polubiło",
},
} as const;
diff --git a/src/layouts/BlogPost.astro b/src/layouts/BlogPost.astro
index 436efde..764cd80 100644
--- a/src/layouts/BlogPost.astro
+++ b/src/layouts/BlogPost.astro
@@ -1,8 +1,7 @@
---
-import { type CollectionEntry, render } from "astro:content";
+import type { CollectionEntry } from "astro:content";
import Masthead from "@/components/blog/Masthead.astro";
-import WebMentions from "@/components/blog/webmentions/index.astro";
import { t } from "@/i18n/translations";
import BaseLayout from "./Base.astro";
@@ -15,8 +14,6 @@ const { post } = Astro.props;
const { ogImage, title, description, updatedDate, publishDate, language } = post.data;
const socialImage = ogImage ?? `/og-image/${post.id}.png`;
const articleDate = updatedDate?.toISOString() ?? publishDate.toISOString();
-const { remarkPluginFrontmatter } = await render(post);
-const readingTime: string = remarkPluginFrontmatter.readingTime;
---
<BaseLayout
@@ -30,13 +27,12 @@ const readingTime: string = remarkPluginFrontmatter.readingTime;
>
<article class="grow break-words" data-pagefind-body>
<div id="blog-hero" class="mb-12">
- <Masthead content={post} readingTime={readingTime} language={language} />
+ <Masthead content={post} language={language} />
</div>
<div
class="prose prose-sm prose-cactus prose-headings:font-semibold prose-headings:text-accent-2 prose-headings:before:absolute prose-headings:before:-ms-4 prose-headings:before:text-gray-600 prose-headings:hover:before:text-accent sm:prose-headings:before:content-['#'] sm:prose-th:before:content-none max-w-none"
>
<slot />
- <WebMentions language={language} />
{
post.data.sourceUrl && (
<p class="mt-8 border-t border-gray-200 pt-6 text-sm dark:border-gray-700">
diff --git a/src/plugins/remark-reading-time.ts b/src/plugins/remark-reading-time.ts
deleted file mode 100644
index 843dde1..0000000
--- a/src/plugins/remark-reading-time.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { toString as mdastToString } from "mdast-util-to-string";
-import getReadingTime from "reading-time";
-
-export function remarkReadingTime() {
- // @ts-expect-error:next-line
- return (tree, { data }) => {
- const textOnPage = mdastToString(tree);
- const readingTime = getReadingTime(textOnPage);
- data.astro.frontmatter.readingTime = readingTime.text;
- };
-}
diff --git a/src/types.ts b/src/types.ts
index ec40902..4177fb1 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -25,60 +25,4 @@ export interface SiteMeta {
lang?: string | undefined;
}
-/** Webmentions */
-export interface WebmentionsFeed {
- children: WebmentionsChildren[];
- name: string;
- type: string;
-}
-
-export interface WebmentionsCache {
- children: WebmentionsChildren[];
- lastFetched: null | string;
-}
-
-export interface WebmentionsChildren {
- author: Author | null;
- content?: Content | null;
- "mention-of": string;
- name?: null | string;
- photo?: null | string[];
- published?: null | string;
- rels?: Rels | null;
- summary?: Summary | null;
- syndication?: null | string[];
- type: string;
- url: string;
- "wm-id": number;
- "wm-private": boolean;
- "wm-property": string;
- "wm-protocol": string;
- "wm-received": string;
- "wm-source": string;
- "wm-target": string;
-}
-
-export interface Author {
- name: string;
- photo: string;
- type: string;
- url: string;
-}
-
-export interface Content {
- "content-type": string;
- html: string;
- text: string;
- value: string;
-}
-
-export interface Rels {
- canonical: string;
-}
-
-export interface Summary {
- "content-type": string;
- value: string;
-}
-
export type AdmonitionType = "tip" | "note" | "important" | "caution" | "warning";
diff --git a/src/utils/webmentions.ts b/src/utils/webmentions.ts
deleted file mode 100644
index 8edfd90..0000000
--- a/src/utils/webmentions.ts
+++ /dev/null
@@ -1,115 +0,0 @@
-import { WEBMENTION_API_KEY } from "astro:env/server";
-import * as fs from "node:fs";
-import type { WebmentionsCache, WebmentionsChildren, WebmentionsFeed } from "@/types";
-
-const DOMAIN = import.meta.env.SITE;
-const CACHE_DIR = ".data";
-const filePath = `${CACHE_DIR}/webmentions.json`;
-const validWebmentionTypes = ["like-of", "mention-of", "in-reply-to"];
-
-const hostName = new URL(DOMAIN).hostname;
-
-// Calls webmention.io api.
-async function fetchWebmentions(timeFrom: string | null, perPage = 1000) {
- if (!DOMAIN) {
- console.warn("No domain specified. Please set in astro.config.ts");
- return null;
- }
-
- if (!WEBMENTION_API_KEY) {
- console.warn("No webmention api token specified in .env");
- return null;
- }
-
- let url = `https://webmention.io/api/mentions.jf2?domain=${hostName}&token=${WEBMENTION_API_KEY}&sort-dir=up&per-page=${perPage}`;
-
- if (timeFrom) url += `&since${timeFrom}`;
-
- const res = await fetch(url);
-
- if (res.ok) {
- const data = (await res.json()) as WebmentionsFeed;
- return data;
- }
-
- return null;
-}
-
-// Merge cached entries [a] with fresh webmentions [b], merge by wm-id
-function mergeWebmentions(a: WebmentionsCache, b: WebmentionsFeed): WebmentionsChildren[] {
- return Array.from(
- [...a.children, ...b.children]
- .reduce((map, obj) => map.set(obj["wm-id"], obj), new Map())
- .values(),
- );
-}
-
-// filter out WebmentionChildren
-export function filterWebmentions(webmentions: WebmentionsChildren[]) {
- return webmentions.filter((webmention) => {
- // make sure the mention has a property so we can sort them later
- if (!validWebmentionTypes.includes(webmention["wm-property"])) return false;
-
- // make sure 'mention-of' or 'in-reply-to' has text content.
- if (webmention["wm-property"] === "mention-of" || webmention["wm-property"] === "in-reply-to") {
- return webmention.content && webmention.content.text !== "";
- }
-
- return true;
- });
-}
-
-// save combined webmentions in cache file
-function writeToCache(data: WebmentionsCache) {
- const fileContent = JSON.stringify(data, null, 2);
-
- // create cache folder if it doesn't exist already
- if (!fs.existsSync(CACHE_DIR)) {
- fs.mkdirSync(CACHE_DIR);
- }
-
- // write data to cache json file
- fs.writeFile(filePath, fileContent, (err) => {
- if (err) throw err;
- console.log(`Webmentions saved to ${filePath}`);
- });
-}
-
-function getFromCache(): WebmentionsCache {
- if (fs.existsSync(filePath)) {
- const data = fs.readFileSync(filePath, "utf-8");
- return JSON.parse(data);
- }
- // no cache found
- return {
- lastFetched: null,
- children: [],
- };
-}
-
-async function getAndCacheWebmentions() {
- const cache = getFromCache();
- const mentions = await fetchWebmentions(cache.lastFetched);
-
- if (mentions) {
- mentions.children = filterWebmentions(mentions.children);
- const webmentions: WebmentionsCache = {
- lastFetched: new Date().toISOString(),
- // Make sure the first arg is the cache
- children: mergeWebmentions(cache, mentions),
- };
-
- writeToCache(webmentions);
- return webmentions;
- }
-
- return cache;
-}
-
-let webMentions: WebmentionsCache;
-
-export async function getWebmentionsForUrl(url: string) {
- if (!webMentions) webMentions = await getAndCacheWebmentions();
-
- return webMentions.children.filter((entry) => entry["wm-target"] === url);
-}