diff options
| author | Joe Carstairs <me@joeac.net> | 2026-06-18 19:21:59 +0100 |
|---|---|---|
| committer | Joe Carstairs <me@joeac.net> | 2026-06-18 19:21:59 +0100 |
| commit | ca8e6824eaa711105f4092365792720c9dde026b (patch) | |
| tree | 2b6493941352d11b799ee63d3f1311c13de649b8 /website/src/components | |
| parent | a0f25057283a397f006127b6f582d190d2c4294e (diff) | |
remove Astro website
Diffstat (limited to 'website/src/components')
| -rw-r--r-- | website/src/components/BaseHead.astro | 54 | ||||
| -rw-r--r-- | website/src/components/BlogFeed.astro | 103 | ||||
| -rw-r--r-- | website/src/components/FormattedDate.astro | 23 | ||||
| -rw-r--r-- | website/src/components/LinksFeed.astro | 78 | ||||
| -rw-r--r-- | website/src/components/Me.astro | 47 | ||||
| -rw-r--r-- | website/src/components/MicrologFeed.astro | 100 | ||||
| -rw-r--r-- | website/src/components/Navbar.astro | 19 | ||||
| -rw-r--r-- | website/src/components/OtpDialog.astro | 31 |
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> |
