diff --git a/src/content.config.ts b/src/content.config.ts
index b7d2f93..21d807f 100644
--- a/src/content.config.ts
+++ b/src/content.config.ts
@@ -10,6 +10,11 @@ export function toSlug(text: string) {
.replace(/^-+|-+$/g, "");
}
+function dateToEST(date: Date, ctx: z.RefinementCtx) {
+ const estOffsetMs = -5 * 60 * 60 * 1000; // est offset (-5 hours)
+ return new Date(date.getTime() - estOffsetMs);
+}
+
const blog = defineCollection({
loader: glob({ base: "./src/posts", pattern: "**/*.{md,mdx}" }),
schema: z
@@ -17,10 +22,7 @@ const blog = defineCollection({
title: z.string(),
slug: z.string().optional(),
description: z.string(),
- date: z.date().transform((date, ctx) => {
- const estOffsetMs = -5 * 60 * 60 * 1000; // est offset (-5 hours)
- return new Date(date.getTime() - estOffsetMs);
- }),
+ date: z.date().transform(dateToEST),
tags: z.array(z.string()).default([]),
image: z.string().optional(),
})
@@ -42,13 +44,22 @@ const projects = defineCollection({
description: z.string(),
tags: z.array(z.string()).default([]),
image: z.string().optional(),
+ startDate: z.date().transform(dateToEST),
+ endDate: z.date().transform(dateToEST).optional(),
})
.transform((object) => {
const slug = object.slug || toSlug(object.title);
const url = object.url || `/projects/${slug}`;
+ const startYear = object.startDate.getFullYear();
+ const endYear = object.endDate?.getFullYear();
+ const date =
+ startYear === endYear || startYear === new Date().getFullYear()
+ ? startYear
+ : `${startYear} – ${endYear ?? "now"}`;
return {
...object,
slug,
+ date,
url,
};
}),
diff --git a/src/pages/projects/[...slug].astro b/src/pages/projects/[...slug].astro
index ab219ec..f6dfeaf 100644
--- a/src/pages/projects/[...slug].astro
+++ b/src/pages/projects/[...slug].astro
@@ -45,22 +45,33 @@ if (frontmatter.data.image) {
/>
)
}
- projects
- /
- {frontmatter.data.title}
-
- {frontmatter.data.description}
-
-
- {frontmatter.data.tags.join(" / ")}
-
+
+
projects
+ /
+ {frontmatter.data.title}
+
+ {frontmatter.data.description}
+
+
+
+
+ {frontmatter.data.tags.join(" / ")}
+
+
+ {frontmatter.data.date}
+
+
diff --git a/src/pages/projects/index.astro b/src/pages/projects/index.astro
index 94bdd2a..65647f0 100644
--- a/src/pages/projects/index.astro
+++ b/src/pages/projects/index.astro
@@ -4,6 +4,14 @@ import Link from "@components/Link.astro";
import { getCollection } from "astro:content";
const projects = await getCollection("projects");
+const farFuture = new Date("3005-12-31");
+projects.sort(
+ (a, b) =>
+ (b.data.endDate ?? farFuture).getTime() -
+ (a.data.endDate || farFuture).getTime() || // sort by end date
+ b.data.startDate.getTime() - a.data.startDate.getTime() ||
+ a.data.title.localeCompare(b.data.title), // then by start year
+);
// const projects = [
// {
// name: "chiffrage",
@@ -52,8 +60,17 @@ const projects = await getCollection("projects");
{
projects.map(({ data: project }) => (
-
{project.title}
- {project.tags &&
· {project.tags.join(" / ")}}
+
+
+ {project.title}
+ {project.tags && (
+ · {project.tags.join(" / ")}
+ )}
+
+
+ {project.date}
+
+
{project.description}
))
diff --git a/src/projects/calore.mdx b/src/projects/calore.mdx
index 7139403..cd3fef3 100644
--- a/src/projects/calore.mdx
+++ b/src/projects/calore.mdx
@@ -2,6 +2,7 @@
title: "calore"
description: "calculate and cache album art color palettes from spotify, musicbrainz, and iTunes"
tags: ["cloudflare workers", "bun", "kv"]
+startDate: 2025-07-01
---
import CurrentAlbum from "@components/CurrentAlbum.astro"
diff --git a/src/projects/cf-colorthief.mdx b/src/projects/cf-colorthief.mdx
index 310896a..3bae496 100644
--- a/src/projects/cf-colorthief.mdx
+++ b/src/projects/cf-colorthief.mdx
@@ -3,4 +3,5 @@ title: "cf-colorthief"
description: "version of colorthief that uses WASM-based image decoding for compatibility with cloudflare workers"
tags: ["typescript", "wasm"]
url: "https://github.com/thrzl/cf-colorthief"
+startDate: 2025-07-01
---
diff --git a/src/projects/chiffrage.mdx b/src/projects/chiffrage.mdx
index 96b8a7a..7f3eff0 100644
--- a/src/projects/chiffrage.mdx
+++ b/src/projects/chiffrage.mdx
@@ -2,6 +2,7 @@
title: "chiffrage"
description: "cross-platform UI for the age encryption format, prioritizing security and good defaults"
tags: ["rust", "tauri", "age"]
+startDate: 2025-11-01
---
import {Image} from "astro:assets"
diff --git a/src/projects/keyage.mdx b/src/projects/keyage.mdx
index fb05134..c77079c 100644
--- a/src/projects/keyage.mdx
+++ b/src/projects/keyage.mdx
@@ -2,6 +2,7 @@
title: "keyage"
description: "an anonymous(*) keyserver for age keys"
tags: ["rust", "age", "sql"]
+startDate: 2026-01-01
---
keyage is a service that stores age public keys.