summaryrefslogtreecommitdiff
path: root/website/src/components
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/components
parenta0f25057283a397f006127b6f582d190d2c4294e (diff)
remove Astro website
Diffstat (limited to 'website/src/components')
-rw-r--r--website/src/components/BaseHead.astro54
-rw-r--r--website/src/components/BlogFeed.astro103
-rw-r--r--website/src/components/FormattedDate.astro23
-rw-r--r--website/src/components/LinksFeed.astro78
-rw-r--r--website/src/components/Me.astro47
-rw-r--r--website/src/components/MicrologFeed.astro100
-rw-r--r--website/src/components/Navbar.astro19
-rw-r--r--website/src/components/OtpDialog.astro31
8 files changed, 0 insertions, 455 deletions
diff --git a/website/src/components/BaseHead.astro b/website/src/components/BaseHead.astro
deleted file mode 100644
index 360c4bb..0000000
--- a/website/src/components/BaseHead.astro
+++ /dev/null
@@ -1,54 +0,0 @@
----
-interface Props {
- title: string;
- description?: string;
- image?: string;
-}
-
-const canonicalURL = new URL(Astro.url.pathname, Astro.site);
-
-const { title, description, image = '/images/headshot.jpg' } = Astro.props;
----
-
-<!-- IndieWeb -->
-<link rel="authorization_endpoint" href="https://indieauth.com/auth">
-<link rel="token_endpoint" href="https://tokens.indieauth.com/token">
-<link rel="micropub" href="https://tasty-windows-lick.loca.lt">
-
-<!-- Stylesheets -->
-<link rel="stylesheet" href="/css/reset.css" />
-<link rel="stylesheet" href="/css/base.css" />
-<link rel="stylesheet" href="/css/hcard.css" />
-<link rel="stylesheet" href="/css/feed.css" />
-
-<!-- Global Metadata -->
-<meta charset="utf-8" />
-<meta name="viewport" content="width=device-width,initial-scale=1" />
-<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
-<meta name="generator" content={Astro.generator} />
-
-<!-- Canonical URL -->
-<link rel="canonical" href={canonicalURL} />
-
-<!-- Primary Meta Tags -->
-<title>{title}</title>
-<meta name="title" content={title} />
-<meta name="description" content={description} />
-
-<!-- Open Graph / Facebook -->
-<meta property="og:type" content="website" />
-<meta property="og:url" content={Astro.url} />
-<meta property="og:title" content={title} />
-<meta property="og:description" content={description} />
-<meta property="og:image" content={new URL(image, Astro.url)} />
-
-<!-- Twitter -->
-<meta property="twitter:card" content="summary_large_image" />
-<meta property="twitter:url" content={Astro.url} />
-<meta property="twitter:title" content={title} />
-<meta property="twitter:description" content={description} />
-<meta property="twitter:image" content={new URL(image, Astro.url)} />
-
-<!-- Feeds -->
-<link rel="alternate" type="text/xml" title="Blog RSS" href="/blog/rss.xml">
-<link rel="alternate" type="text/xml" title="Links RSS" href="/links/rss.xml">
diff --git a/website/src/components/BlogFeed.astro b/website/src/components/BlogFeed.astro
deleted file mode 100644
index 552fff4..0000000
--- a/website/src/components/BlogFeed.astro
+++ /dev/null
@@ -1,103 +0,0 @@
----
-import type { CollectionEntry } from 'astro:content';
-import { getCollection } from 'astro:content';
-import FormattedDate from './FormattedDate.astro';
-
-export interface Props {
- headingLevel?: 1 | 2 | 3 | 4 | 5 | 6,
- hideAuthor?: boolean,
- hideSubheadings?: boolean,
- maxEntries?: number,
-};
-
-const { headingLevel = 2, hideSubheadings = false, hideAuthor = false, maxEntries } = Astro.props;
-
-const allPosts = (await getCollection('blog')).filter((post) => !post.data.hidden);
-
-const posts = maxEntries === undefined
- ? allPosts
- : allPosts.sort(sortByPubDateDescending).slice(0, maxEntries);
-
-const distinctYears: number[] = posts
- .map(post => pubDate(post).getFullYear())
- .reduce<number[]>((acc, curr) => acc.includes(curr) ? acc : [...acc, curr], [])
- .sort((a, b) => b - a);
-
-function matchesYear(year: number) {
- return (post: CollectionEntry<'blog'>) => pubDate(post).getFullYear() === year;
-}
-
-function sortByPubDateDescending(post1: CollectionEntry<'blog'>, post2: CollectionEntry<'blog'>) {
- const date1 = pubDate(post1).getTime();
- const date2 = pubDate(post2).getTime();
- return date2 - date1;
-}
-
-function pubDate(post: CollectionEntry<'blog'>): Date {
- if (post.data.pubDate) {
- return post.data.pubDate;
- }
- const date = new Date(post.id);
- if (isNaN(date.valueOf())) {
- throw new Error(`Post ${post.id} does not have a valid publication date.`);
- }
- return date;
-}
-
-const HeadingElem = `h${headingLevel} class="p-name"`;
-const SubHeadingElem = `h${headingLevel + 1}`;
-const SubSubHeadingElem = `h${headingLevel + 2}`;
-const AuthorElem = `p${hideAuthor ? " hidden" : ""}`;
-
-const canonicalBlogUrl = new URL('blog', Astro.site)
----
-
-<section class="h-feed">
- <HeadingElem>
- My blog
- </HeadingElem>
-
- <aside>
- <AuthorElem>
- This blog is written by <a class="p-author h-card" href="/">Joe Carstairs</a>
- </AuthorElem>
-
- <p hidden>
- <a class="u-url" href={canonicalBlogUrl}>Permalink</a>
- </p>
- </aside>
-
- <slot />
-
- { hideSubheadings
- ? <ul>
- { posts.sort(sortByPubDateDescending).map(post => (
- <li class="h-entry">
- <SubSubHeadingElem>
- <a class="u-url p-name" href={`/blog/${post.id}`}>{post.data.title}</a>
- </SubSubHeadingElem>
- <p class="p-summary" set:html={post.data.description} />
- <FormattedDate className="dt-published" date={pubDate(post)} />
- </li>
- )) }
- </ul>
- : distinctYears.map(year => (
- <SubHeadingElem>{year}</SubHeadingElem>
- <ul>
- { posts.filter(matchesYear(year)).sort(sortByPubDateDescending).map(post => (
- <li class="h-entry">
- <SubSubHeadingElem>
- <a class="u-url p-name" href={`/blog/${post.id}`}>{post.data.title}</a>
- </SubSubHeadingElem>
- <p class="p-summary" set:html={post.data.description} />
- <FormattedDate className="dt-published" date={pubDate(post)} />
- </li>
- )) }
- </ul>
- )) }
-
- { (maxEntries !== undefined && maxEntries < allPosts.length)
- ? <p class="full-feed-link"><a href="/blog">All blog posts</a></p>
- : <></>
- }
-</section>
diff --git a/website/src/components/FormattedDate.astro b/website/src/components/FormattedDate.astro
deleted file mode 100644
index ce700f1..0000000
--- a/website/src/components/FormattedDate.astro
+++ /dev/null
@@ -1,23 +0,0 @@
----
-interface Props {
- className?: string;
- date: Date | string;
- hidden?: boolean;
-}
-
-let { className, date, hidden } = Astro.props;
-if (typeof(date) === 'string') {
- date = new Date(date);
-}
-const dateStr =
- date.toLocaleDateString('en-GB', {
- year: 'numeric',
- month: 'long',
- day: 'numeric',
- });
----
-
-{ hidden
- ? <time datetime={date.toISOString()} class={className ?? ''} hidden>{dateStr}</time>
- : <time datetime={date.toISOString()} class={className ?? ''}>{dateStr}</time>
-}
diff --git a/website/src/components/LinksFeed.astro b/website/src/components/LinksFeed.astro
deleted file mode 100644
index 1d837f0..0000000
--- a/website/src/components/LinksFeed.astro
+++ /dev/null
@@ -1,78 +0,0 @@
----
-import FormattedDate from '../components/FormattedDate.astro';
-import allLinks from '../data/links.ts';
-
-type Link = (typeof allLinks)[number];
-
-export interface Props {
- headingLevel?: 1 | 2 | 3 | 4 | 5 | 6,
- hideAuthor?: boolean,
- maxEntries?: number,
-};
-
-const { headingLevel = 2, hideAuthor = false, maxEntries } = Astro.props;
-
-const headingElem = `h${headingLevel}`;
-const subHeadingElem = `h${headingLevel + 1}`
-
-const links: Link[] = maxEntries === undefined
- ? allLinks
- : allLinks.sort(sortByDateAddedDescending).slice(0, maxEntries);
-
-const distinctYears: string[] = links
- .map(link => link.isoDateAdded.slice(0,4))
- .reduce<string[]>((acc, curr) => acc.includes(curr) ? acc : [...acc, curr], [])
- .sort((a, b) => Number.parseInt(b) - Number.parseInt(a));
-
-function matchesYear(year: string) {
- return (link: (typeof links)[number]) => link.isoDateAdded.slice(0,4) === year;
-}
-
-function sortByDateAddedDescending(link1: (typeof links)[number], link2: (typeof links)[number]) {
- const date1 = new Date(link1.isoDateAdded).getTime();
- const date2 = new Date(link2.isoDateAdded).getTime();
- return date2 - date1;
-}
-
-const canonicalLinksUrl = new URL('links', Astro.site)
----
-
-<section class="h-feed">
- <Fragment set:html={`
- <${headingElem} class="p-name">
- My links
- </${headingElem}>
- `} />
-
- <aside>
- <p hidden={hideAuthor}>
- These links are collected by <a class="p-author h-card" href="/">Joe Carstairs</a>
- </p>
-
- <p hidden>
- <a class="u-url" href={canonicalLinksUrl}>Permalink</a>
- </p>
- </aside>
-
- { distinctYears.map(year => (
- <Fragment set:html={`
- <${subHeadingElem}>
- ${year}
- </${subHeadingElem}>
- `} />
- <ul>
- { links.filter(matchesYear(year)).sort(sortByDateAddedDescending).map(link => (
- <li class="h-entry e-content">
- <FormattedDate className="dt-published" date={link.isoDateAdded} />
- <a class="u-url p-name" href={link.href} set:html={link.title} />
- <p class="p-description" set:html={link.description} />
- </li>
- )) }
- </ul>
- )) }
-
- { (maxEntries !== undefined && maxEntries < allLinks.length)
- ? <p class="full-feed-link"><a href="/links">All links</a></p>
- : <></>
- }
-</section>
diff --git a/website/src/components/Me.astro b/website/src/components/Me.astro
deleted file mode 100644
index 71d2f10..0000000
--- a/website/src/components/Me.astro
+++ /dev/null
@@ -1,47 +0,0 @@
----
----
-
-<section class="h-card">
- <div>
- <img class="u-photo" src="/images/headshot.webp" height="96" width="96" />
- </div>
-
- <header>
- <h1>
- Joe Carstairs
- </h1>
-
- <div hidden>
- <a class="p-name u-url u-uid" href="https://joeac.net" rel="me">
- Permalink
- </a>
- </div>
- </header>
-
- <div class="h-card__text">
- <p>
- Hi! šŸ‘‹ My name is <span class="p-given-name">Joe</span>
- <span class="p-family-name">Carstairs</span>. I’m a
- <span class="p-job-title">software developer</span> at
- <a class="p-org" href="https://www.scottlogic.com">Scott Logic</a>,
- a Divinity student at the University of Edinburgh,
- a lapsed fiddle player and a very occasional poet.
- </p>
-
- <p>
- HMU with your thoughts on Elizabethan poetry, connecting to the
- Internet, and the Apocalypse of Saint John.
- </p>
-
- <p>
- <small>
- Or get me on
- <a href="https://www.facebook.com/joe.carstairs.5" rel="me">Facebook</a>,
- <a href="https://mastodon.social/@joe_carstairs" rel="me">Mastodon</a>,
- <a href="https://www.linkedin.com/in/joe-carstairs-0aa936277" rel="me">LinkedIn</a>,
- <a href="https://bsky.app/profile/joeacarstairs.bsky.social" rel="me">BlueSky</a>,
- or <a href="https://github.com/joeacarstairs" rel="me">GitHub</a>.
- </small>
- </p>
- </div>
-</section>
diff --git a/website/src/components/MicrologFeed.astro b/website/src/components/MicrologFeed.astro
deleted file mode 100644
index b4941b1..0000000
--- a/website/src/components/MicrologFeed.astro
+++ /dev/null
@@ -1,100 +0,0 @@
----
-import type { CollectionEntry } from 'astro:content';
-import { getCollection } from 'astro:content';
-import FormattedDate from './FormattedDate.astro';
-import { renderGemtextToHtml } from '../renderGemtextToHtml';
-
-export interface Props {
- headingLevel?: 1 | 2 | 3 | 4 | 5 | 6,
- hideAuthor?: boolean,
- hideSubheadings?: boolean,
- maxEntries?: number,
-};
-
-const { headingLevel = 2, hideSubheadings = false, hideAuthor = false, maxEntries } = Astro.props;
-
-const allPosts = await getCollection('microlog');
-
-const posts = maxEntries === undefined
- ? allPosts
- : allPosts.sort(sortByIdDescending).slice(0, maxEntries);
-
-const distinctYears: number[] = posts
- .map(post => pubDate(post).getFullYear())
- .reduce<number[]>((acc, curr) => acc.includes(curr) ? acc : [...acc, curr], [])
- .sort((a, b) => b - a);
-
-function matchesYear(year: number) {
- return (post: CollectionEntry<'microlog'>) => pubDate(post).getFullYear() === year;
-}
-
-function sortByIdDescending(post1: CollectionEntry<'microlog'>, post2: CollectionEntry<'microlog'>) {
- return post2.id.localeCompare(post1.id);
-}
-
-function pubDate(post: CollectionEntry<'microlog'>): Date {
- console.warn(post.id);
- const date = new Date(post.id.replace(/\.[0-9]+$/g, ''));
- if (isNaN(date.valueOf())) {
- throw new Error(`Post ${post.id} does not have a valid publication date.`);
- }
- return date;
-}
-
-const HeadingElem = `h${headingLevel} class="p-name"`;
-const SubHeadingElem = `h${headingLevel + 1}`;
-const SubSubHeadingElem = `h${headingLevel + 2}`;
-const AuthorElem = `p${hideAuthor ? " hidden" : ""}`;
-
-const canonicalMicrologUrl = new URL('microlog', Astro.site)
----
-
-<section class="h-feed">
- <HeadingElem>
- My microlog
- </HeadingElem>
-
- <aside>
- <AuthorElem>
- This microlog is written by <a class="p-author h-card" href="/">Joe Carstairs</a>
- </AuthorElem>
-
- <p hidden>
- <a class="u-url" href={canonicalMicrologUrl}>Permalink</a>
- </p>
- </aside>
-
- <slot />
-
- { hideSubheadings
- ? <ul>
- { posts.sort(sortByIdDescending).map(post => (
- <li class="h-entry">
- <SubSubHeadingElem>
- <a class="u-url p-name" href={`/microlog/${post.id}`}>{post.id}</a>
- </SubSubHeadingElem>
- <section class="e-content" set:html={post.body} />
- <FormattedDate hidden={true} className="dt-published" date={pubDate(post)} />
- </li>
- )) }
- </ul>
- : distinctYears.map(year => (
- <SubHeadingElem>{year}</SubHeadingElem>
- <ul>
- { posts.filter(matchesYear(year)).sort(sortByIdDescending).map(post => (
- <li class="h-entry">
- <SubSubHeadingElem>
- <a class="u-url p-name" href={`/microlog/${post.id}`}>{post.id}</a>
- </SubSubHeadingElem>
- <section class="e-content" set:html={renderGemtextToHtml(post.body ?? '')} />
- <FormattedDate hidden={true} className="dt-published" date={pubDate(post)} />
- </li>
- )) }
- </ul>
- )) }
-
- { (maxEntries !== undefined && maxEntries < allPosts.length)
- ? <p class="full-feed-link"><a href="/microlog">All microlog posts</a></p>
- : <></>
- }
-</section>
diff --git a/website/src/components/Navbar.astro b/website/src/components/Navbar.astro
deleted file mode 100644
index 0fe5b32..0000000
--- a/website/src/components/Navbar.astro
+++ /dev/null
@@ -1,19 +0,0 @@
----
----
-
-<nav>
- <ul>
- <li>
- <a href="/">Home</a>
- </li>
- <li>
- <a href="/blog">Blog</a>
- </li>
- <li>
- <a href="/microlog">Microlog</a>
- </li>
- <li>
- <a href="/contact">Contact</a>
- </li>
- </ul>
-</nav>
diff --git a/website/src/components/OtpDialog.astro b/website/src/components/OtpDialog.astro
deleted file mode 100644
index 1fd5fe9..0000000
--- a/website/src/components/OtpDialog.astro
+++ /dev/null
@@ -1,31 +0,0 @@
----
----
-
-<script src="../scripts/otp-form-wc.ts"></script>
-<link rel="stylesheet" href="/css/otp.css" />
-
-<dialog class="otp-dialog">
- <otp-form>
- <form class="otp-form" class="otp-form">
- <p>
- I've sent a six-digit code to <span class="otp-recipient">your email address</span>.
- Let me know what it is, so I can confirm this really is your email address. The
- code will be valid <span class="otp-valid-until">for five minutes</span>.
- </p>
-
- <p hidden class="error"/>
-
- <div class="otp-inputs">
- <input maxlength="1" name="1" required autocapitalize="characters" autocomplete="one-time-code" aria-label="First digit of one-time passcode" autofocus>
- <input maxlength="1" name="2" required autocapitalize="characters" autocomplete="one-time-code" aria-label="Second digit of one-time passcode">
- <input maxlength="1" name="3" required autocapitalize="characters" autocomplete="one-time-code" aria-label="Third digit of one-time passcode">
- <input maxlength="1" name="4" required autocapitalize="characters" autocomplete="one-time-code" aria-label="Fourth digit of one-time passcode">
- <input maxlength="1" name="5" required autocapitalize="characters" autocomplete="one-time-code" aria-label="Fifth digit of one-time passcode">
- <input maxlength="1" name="6" required autocapitalize="characters" autocomplete="one-time-code" aria-label="Sixth digit of one-time passcode">
- </div>
-
- <input type="submit" value="Verify">
- <button disabled class="resend-button">Resend (60s)</button>
- </form>
- </otp-form>
-</dialog>