summaryrefslogtreecommitdiff
path: root/website/src/pages
diff options
context:
space:
mode:
authorJoe Carstairs <me@joeac.net>2026-06-18 19:21:59 +0100
committerJoe Carstairs <me@joeac.net>2026-06-18 19:21:59 +0100
commitca8e6824eaa711105f4092365792720c9dde026b (patch)
tree2b6493941352d11b799ee63d3f1311c13de649b8 /website/src/pages
parenta0f25057283a397f006127b6f582d190d2c4294e (diff)
remove Astro website
Diffstat (limited to 'website/src/pages')
-rw-r--r--website/src/pages/blog/[...slug].astro28
-rw-r--r--website/src/pages/blog/index.astro23
-rw-r--r--website/src/pages/blog/rss.xml.ts37
-rw-r--r--website/src/pages/blog/subscribe.astro90
-rw-r--r--website/src/pages/contact.astro97
-rw-r--r--website/src/pages/error.astro14
-rw-r--r--website/src/pages/index.astro11
-rw-r--r--website/src/pages/links/rss.xml.ts33
-rw-r--r--website/src/pages/microlog/[...slug].astro26
-rw-r--r--website/src/pages/microlog/index.astro13
-rw-r--r--website/src/pages/microlog/rss.xml.ts29
11 files changed, 0 insertions, 401 deletions
diff --git a/website/src/pages/blog/[...slug].astro b/website/src/pages/blog/[...slug].astro
deleted file mode 100644
index 54cf9ac..0000000
--- a/website/src/pages/blog/[...slug].astro
+++ /dev/null
@@ -1,28 +0,0 @@
----
-import { type CollectionEntry, getCollection, render } from 'astro:content';
-import BlogPost from '../../layouts/BlogPost.astro';
-import { renderGemtextToHtml } from '../../renderGemtextToHtml';
-
-export async function getStaticPaths() {
- const posts = await getCollection('blog');
- return posts.map((post) => ({
- params: { slug: post.id },
- props: post,
- }));
-}
-type Props = CollectionEntry<'blog'>;
-
-const post = Astro.props;
-const Content = post.filePath?.endsWith('.gmi')
- ? renderGemtextToHtml(post.body ?? '', post.data.title)
- : (await render(post)).Content;
----
-
-<BlogPost
- title={post.data.title}
- description={post.data.description}
- pubDate={post.data.pubDate ?? new Date(post.id)}
- updatedDate={post.data.updatedDate}
->
- { post.filePath?.endsWith('.gmi') ? <Fragment set:html={Content}></Fragment> : <Content />}
-</BlogPost>
diff --git a/website/src/pages/blog/index.astro b/website/src/pages/blog/index.astro
deleted file mode 100644
index df758ed..0000000
--- a/website/src/pages/blog/index.astro
+++ /dev/null
@@ -1,23 +0,0 @@
----
-import BaseHead from '../../components/BaseHead.astro';
-import { SITE_TITLE, SITE_DESCRIPTION } from '../../consts';
-import BlogFeed from '../../components/BlogFeed.astro';
-import Navbar from '../../components/Navbar.astro';
----
-
-<!doctype html>
-<html lang="en">
- <head>
- <BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
- </head>
- <body>
- <Navbar />
- <main>
- <BlogFeed headingLevel={1}>
- <section>
- <p><a href="/blog/subscribe">How to subscribe to this blog</a></p>
- </section>
- </BlogFeed>
- </main>
- </body>
-</html>
diff --git a/website/src/pages/blog/rss.xml.ts b/website/src/pages/blog/rss.xml.ts
deleted file mode 100644
index 5b90fd0..0000000
--- a/website/src/pages/blog/rss.xml.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import path from 'node:path';
-import rss from '@astrojs/rss';
-import type { APIContext } from 'astro';
-import { getCollection } from 'astro:content';
-import MarkdownIt from 'markdown-it';
-
-const mdParser = new MarkdownIt({
- html: true
-});
-
-export async function GET(context: APIContext) {
- // `site` is guaranteed to exist because we define it in our Astro config
- const site = context.site!;
- const posts = await getCollection('blog');
-
- return rss({
- title: 'Joe Carstairs’ blog',
- description: 'Short posts on random topics I find interesting',
- customData: `
- <image>/images/headshot.webp</image>
- <language>en-GB</language>
- `,
- site: path.join(site.toString(), 'blog'),
- trailingSlash: false,
- items: posts.map((post) => ({
- link: `/blog/${post.id}`,
- title: post.data.title,
- content: mdParser.render(post.body ?? ''),
- pubDate: post.data.pubDate,
- description: post.data.description,
- author: 'Joe Carstairs',
-
- // A page for displaying comments related to the item
- /* commentsUrl: `${post.slug}/comments`, */
- })),
- })
-}
diff --git a/website/src/pages/blog/subscribe.astro b/website/src/pages/blog/subscribe.astro
deleted file mode 100644
index cbb6237..0000000
--- a/website/src/pages/blog/subscribe.astro
+++ /dev/null
@@ -1,90 +0,0 @@
----
-import Page from "../../layouts/Page.astro";
-
----
-
-<Page
- title="How to subscribe to joeac's blog"
- description="How to subscribe to joeac's blog"
->
- <section>
- <h1>How to subscribe to my blog</h1>
-
- <p>
- The short version: use my <a href="/blog/rss.xml">RSS file</a> or my
- <a href="gemini://joeac.net/logs/longlog.gmi">gemlog</a>.
- </p>
-
- <p>
- If neither of those things means anything to you, no worries. You could
- just look at my website every once in a while, but that gets old. A better
- way is to use my RSS file. Here's how.
- </p>
-
- <p>
- An RSS file, if you don't know, is a file containing a list of posts in a
- format that's so stupid even computers can understand it. Mine is at
- <a href="https://joeac.net/blog/rss.xml">https://joeac.net/blog/rss.xml</a>.
- Since computers can understand it, you can get software to read it
- automatically and tell you when I've made a new blog post. That's much less
- hassle than having to check manually yourself!
- </p>
-
- <p>
- I'm not the only person serving RSS files, of course. You might not have
- heard of them, but they're actually all over the Web. In the early days of
- the Web, everyone was using them, so they've ended up built in to a whole
- load of foundational Web technology, and hence ubiquitous, even if most
- Web users have forgotten they exist. Once you've subscribed to my blog,
- you can subscribe to basically any blog, podcast, or YouTube channel, and
- a lot of other Websites for news, organisations, whatever. Point a
- well-designed RSS aggregator or RSS reader at most Websites, and it'll
- automatically find an associated RSS feed.
- </p>
-
- <p>
- If you're not persuaded, try reading
- <a href="https://doctorow.medium.com/you-should-be-using-an-rss-reader-76aed31151f9">
- Cory Doctorow explaining why RSS is ridiculously good
- </a>. He's terrifyingly persuasive. Also, read his novels!
- </p>
-
- <p>
- If you just want to give it a go quickly, probably the quickest way is to
- use someone else's RSS aggregator that they'll let you use as a service.
- <a href="https://feedly.com/news-reader">Feedly</a> and
- <a href="https://www.inoreader.com">Inoreader</a> have free tiers.
- </p>
-
- <p>
- If you want to have a little more control, you can self-host an RSS
- reader, like <a href="https://tt-rss.org">Tiny Tiny RSS</a>,
- <a href="https://miniflux.app">Miniflux</a>, or
- <a href="https://freshrss.org">FreshRSS</a>. But why would you do that?
- Self-hosting is complicated! Why not just use a normal, local app? Here are
- some options for that you might want to investigate. All these are active
- free and open-source software projects at the time of writing.
- </p>
-
- <ul>
- <li><a href="https://hyliu.me/fluent-reader">Fluent Reader</a> (Linux, Windows, Mac, also mobile I think?)</li>
- <li><a href="https://www.rssowl.org">RSSOwl</a> (Linux, Windows, Mac)</li>
- <li><a href="https://capyreader.com">Capy Reader</a> (Android)</li>
- <li><a href="https://apps.kde.org/en-gb/akregator">Akregator</a> (Linux, Windows (unstable))</li>
- <li><a href="https://gfeeds.gabmus.org">GFeeds</a> (Linux)</li>
- <li><a href="https://lzone.de/liferea">Liferea</a> (Linux)</li>
- </ul>
-
- <p>
- For the terminally geeky, there is also
- <a href="https://newsboat.org">Newsboat</a>. I used it for ages, it works
- fabulously, but it's all in the terminal, so you have to be OK with that.
- </p>
-
- <p>
- You can also just enter 'RSS feed reader' into your favourite search
- engine, software distribution, or app store and see what comes up. Try
- adding 'FOSS' into the query to prioritise free, open-source software.
- </p>
- </section>
-</Page>
diff --git a/website/src/pages/contact.astro b/website/src/pages/contact.astro
deleted file mode 100644
index 85a5155..0000000
--- a/website/src/pages/contact.astro
+++ /dev/null
@@ -1,97 +0,0 @@
----
-import OtpDialog from "../components/OtpDialog.astro";
-import Page from "../layouts/Page.astro";
-export const prerender = false;
----
-
-<Page title="Contact" description="Contact Joe Carstairs">
- <OtpDialog />
-
- <form class="contact-form">
- <h1>Contact me</h1>
- <p hidden class="error"/>
- <label for="name">Name</label>
- <input id="name" name="name" type="text" required>
- <label for="email">Email</label>
- <input id="email" name="email" type="email" required>
- <label for="message">Message</label>
- <textarea id="message" name="message" required></textarea>
- <input type="submit" value="Send">
- </form>
-
- <section class="success" hidden="true">
- <h1>You sent me a message!</h1>
- <p>Thanks for that. I may be in touch.</p>
- <p>In case you forgot, your message was this:</p>
- <hr>
- <dl>
- <dt>Name</dt> <dd class="sentname">???</dd>
- <dt>Email</dt> <dd class="sentemail">???</dd>
- <dt>Message</dt> <dd class="sentmessage">???</dd>
- </dl>
- </section>
-
- <script>
- import { resendOtp } from '../scripts/contact/resend-otp';
- import { submitContactForm } from '../scripts/contact/submit-contact-form';
- import { submitOtpForm } from '../scripts/contact/submit-otp-form';
- import type { Selectors } from '../scripts/contact/selectors';
-
- function locateOrPanic<T extends Element>(selector: string, desc: string, root?: Element): T {
- const elem = (root ?? document).querySelector<T>(selector);
- if (!elem) {
- alert(`Technical error: could not locate ${desc}. Please let Joe know if you have another means of contacting him.`);
- throw new Error(`Could not locate ${desc}`);
- }
- return elem;
- }
-
- const contactForm = locateOrPanic<HTMLFormElement>('form.contact-form', 'contact form');
- const otpDialog = locateOrPanic<HTMLDialogElement>('dialog.otp-dialog', 'OTP dialog');
- const otpForm = locateOrPanic<HTMLFormElement>('form.otp-form', 'OTP form');
- const successSection = document.querySelector('section.success');
- let resendButtonInterval: undefined | NodeJS.Timeout = undefined;
-
- const selectors: Selectors = {
- contactForm: {
- emailElem: () => locateOrPanic<HTMLInputElement>('input[name="email"]', 'email input', contactForm),
- errorElem: () => contactForm.querySelector('.error'),
- nameElem: () => locateOrPanic<HTMLInputElement>('input[name="name"]', 'name input', contactForm),
- messageElem: () => locateOrPanic<HTMLTextAreaElement>('textarea[name="message"]', 'message textarea', contactForm),
- self: () => contactForm,
- submitButton: () => contactForm.querySelector('input[type="submit"]'),
- },
- otpDialog: {
- allOtpInputs: () => otpForm.querySelectorAll('input:not([type="submit"])'),
- errorElem: () => otpDialog.querySelector('.error'),
- firstOtpInput: () => otpForm.querySelector('input:not([type="submit"]):first-child'),
- otpForm: () => otpForm,
- otpRecipient: () => otpDialog.querySelector('.otp-recipient'),
- otpValidUntil: () => otpDialog.querySelector('.otp-valid-until'),
- resendButton: () => otpDialog.querySelector<HTMLButtonElement>('button.resend-button'),
- self: () => otpDialog,
- submitButton: () => otpForm.querySelector('input[type="submit"]'),
- },
- successSection: {
- email: () => successSection?.querySelector('.sentemail') ?? null,
- name: () => successSection?.querySelector('.sentname') ?? null,
- message: () => successSection?.querySelector('.sentmessage') ?? null,
- self: () => successSection,
- }
- };
-
- contactForm.addEventListener('submit', async (event) => {
- event.preventDefault();
- ({ resendButtonInterval } = await submitContactForm(selectors, resendButtonInterval));
- });
-
- selectors.otpDialog.resendButton()?.addEventListener('click', async () => {
- ({ resendButtonInterval } = await resendOtp(selectors, resendButtonInterval));
- });
-
- otpForm.addEventListener('submit', async (event) => {
- event.preventDefault();
- await submitOtpForm(selectors);
- });
- </script>
-</Page>
diff --git a/website/src/pages/error.astro b/website/src/pages/error.astro
deleted file mode 100644
index dd7188a..0000000
--- a/website/src/pages/error.astro
+++ /dev/null
@@ -1,14 +0,0 @@
----
-import Page from '../layouts/Page.astro';
----
-
-<Page title="Got lost?" description="Error page for Joe's personal website">
- <section>
- <h1>Got lost?</h1>
-
- <p>
- If you’re on this page, something’s probably gone wrong. Try going to
- my <a href="/">homepage</a> instead.
- </p>
- </section>
-</Page>
diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro
deleted file mode 100644
index 8f26424..0000000
--- a/website/src/pages/index.astro
+++ /dev/null
@@ -1,11 +0,0 @@
----
-import BlogFeed from '../components/BlogFeed.astro';
-import LinksFeed from '../components/LinksFeed.astro';
-import Me from '../components/Me.astro';
-import { SITE_TITLE, SITE_DESCRIPTION } from '../consts';
-import Page from '../layouts/Page.astro';
----
-
-<Page title={SITE_TITLE} description={SITE_DESCRIPTION}>
- <Me />
-</Page>
diff --git a/website/src/pages/links/rss.xml.ts b/website/src/pages/links/rss.xml.ts
deleted file mode 100644
index 3f9e9a0..0000000
--- a/website/src/pages/links/rss.xml.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import path from 'node:path';
-import rss from '@astrojs/rss';
-import type { APIContext } from 'astro';
-import LINKS from '../../data/links.ts';
-import MarkdownIt from 'markdown-it';
-
-const mdParser = new MarkdownIt({
- html: true
-});
-
-export async function GET(context: APIContext) {
- // `site` is guaranteed to exist because we define it in our Astro config
- const site = context.site!;
-
- return rss({
- title: 'Joe Carstairs’ links',
- description: 'An assortment of links I’m accumulating.',
- customData: `
- <image>/images/headshot.webp</image>
- <language>en-GB</language>
- `,
- site: path.join(site.toString(), 'links'),
- trailingSlash: false,
- items: LINKS.map((link) => ({
- link: link.href,
- title: link.title,
- content: mdParser.render(link.description),
- pubDate: new Date(link.isoDateAdded),
- description: mdParser.render(link.description),
- author: 'Joe Carstairs',
- })),
- })
-}
diff --git a/website/src/pages/microlog/[...slug].astro b/website/src/pages/microlog/[...slug].astro
deleted file mode 100644
index 69c94b0..0000000
--- a/website/src/pages/microlog/[...slug].astro
+++ /dev/null
@@ -1,26 +0,0 @@
----
-import { type CollectionEntry, getCollection, render } from 'astro:content';
-import MicrologPost from '../../layouts/MicrologPost.astro';
-import { renderGemtextToHtml } from '../../renderGemtextToHtml';
-
-export async function getStaticPaths() {
- const posts = await getCollection('microlog');
- return posts.map((post) => ({
- params: { slug: post.id },
- props: post,
- }));
-}
-type Props = CollectionEntry<'microlog'>;
-
-const { id: slug, body } = Astro.props;
-const pubDateStr = slug.replaceAll(/\.[0-9]+/g, '');
-const pubDate = new Date(pubDateStr);
-if (isNaN(pubDate.valueOf())) {
- throw new Error(`Could not construct a valid publication date for microlog post: ${slug}`);
-}
-const Content = renderGemtextToHtml(body ?? '', pubDateStr);
----
-
-<MicrologPost pubDate={pubDate} title={pubDateStr}>
- <Fragment set:html={Content}></Fragment>
-</MicrologPost>
diff --git a/website/src/pages/microlog/index.astro b/website/src/pages/microlog/index.astro
deleted file mode 100644
index dd1ee16..0000000
--- a/website/src/pages/microlog/index.astro
+++ /dev/null
@@ -1,13 +0,0 @@
----
-import { SITE_TITLE, SITE_DESCRIPTION } from '../../consts';
-import MicrologFeed from '../../components/MicrologFeed.astro';
-import Page from '../../layouts/Page.astro';
----
-
-<Page title={SITE_TITLE} description={SITE_DESCRIPTION}>
- <MicrologFeed headingLevel={1}>
- <!-- <section>
- <p><a href="/blog/subscribe">How to subscribe to this blog</a></p>
- </section> -->
- </MicrologFeed>
-</Page>
diff --git a/website/src/pages/microlog/rss.xml.ts b/website/src/pages/microlog/rss.xml.ts
deleted file mode 100644
index fa04b32..0000000
--- a/website/src/pages/microlog/rss.xml.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import path from "node:path";
-import rss from "@astrojs/rss";
-import type { APIContext } from "astro";
-import { getCollection } from "astro:content";
-import { renderGemtextToHtml } from "../../renderGemtextToHtml";
-
-export async function GET(context: APIContext) {
- // `site` is guaranteed to exist because we define it in our Astro config
- const site = context.site!;
- const posts = await getCollection("microlog");
-
- return rss({
- title: "Joe Carstairs’ microlog",
- description: "My private X feed I guess?",
- customData: `
- <image>/images/headshot.webp</image>
- <language>en-GB</language>
- `,
- site: path.join(site.toString(), "microlog"),
- trailingSlash: false,
- items: posts.map((post) => ({
- link: `/microlog/${post.id}`,
- title: post.id,
- content: renderGemtextToHtml(post.body ?? ""),
- pubDate: new Date(post.id.replace(/\.[0-9]+$/g, "")),
- author: "Joe Carstairs",
- })),
- });
-}