initial: four-collection content pipeline for unique.rzen.dev

A small Astro + MDX blog for delightful, daily-use finds. The architecture
separates capture from publication and supports reuse:

- find/    captured items, hidden from indexes
- finds/   editorial superposts referencing find slugs in a grid
- reviews/ full-length writeups; can graduate from a find via fromFind
- posts/   regular essays

Three project-local skills under .claude/skills/ drive the pipeline:
daily-finds (capture-only sweep across sources.json), build-finds (draft
a thematic superpost), build-review (graduate a find).

sources.json carries 47 curated feeds with per-source fetch strategy
(webfetch / curl / skip) so Cloudflare-blocked sites and Reddit JSON
endpoints are handled deterministically. /sources renders a 3-column
card stream with a "Recent finds" cross-reference per source.

Seeds: 17 reviews and one superpost referencing 5 finds.
This commit is contained in:
2026-05-03 11:48:03 -04:00
commit 9858abedc2
95 changed files with 10230 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
import { getCollection, type CollectionEntry } from 'astro:content';
export type FindsPost = CollectionEntry<'finds'>;
export async function findsReferencing(slug: string): Promise<FindsPost[]> {
const all = await getCollection('finds');
return all
.filter((p) => p.data.items.includes(slug))
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
}
+52
View File
@@ -0,0 +1,52 @@
import { getCollection } from 'astro:content';
export type Entry = {
type: 'review' | 'post' | 'finds';
id: string;
href: string;
date: Date;
category: string;
tags: string[];
primary: string;
secondary?: string;
};
export async function getAllEntries(): Promise<Entry[]> {
const reviews = await getCollection('reviews');
const posts = await getCollection('posts');
const findsPosts = await getCollection('finds');
const r: Entry[] = reviews.map((e) => ({
type: 'review',
id: e.id,
href: `/reviews/${e.id}/`,
date: e.data.date,
category: e.data.category,
tags: e.data.tags,
primary: e.data.name,
secondary: e.data.subtitle,
}));
const p: Entry[] = posts.map((e) => ({
type: 'post',
id: e.id,
href: `/posts/${e.id}/`,
date: e.data.date,
category: e.data.category,
tags: e.data.tags,
primary: e.data.title,
}));
const f: Entry[] = findsPosts.map((e) => ({
type: 'finds',
id: e.id,
href: `/finds/${e.id}/`,
date: e.data.date,
category: e.data.category,
tags: e.data.tags,
primary: e.data.title,
secondary: e.data.blurb,
}));
return [...r, ...p, ...f].sort((a, b) => b.date.valueOf() - a.date.valueOf());
}
+24
View File
@@ -0,0 +1,24 @@
import { getCollection, type CollectionEntry } from 'astro:content';
export type FindItem = CollectionEntry<'find'>;
export async function getAllFinds(): Promise<FindItem[]> {
const items = await getCollection('find');
return items.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
}
export async function resolveFinds(slugs: string[]): Promise<FindItem[]> {
const all = await getCollection('find');
const bySlug = new Map(all.map((f) => [f.id, f]));
return slugs
.map((s) => bySlug.get(s))
.filter((f): f is FindItem => Boolean(f));
}
export async function recentFindsBySource(
sourceName: string,
limit = 5
): Promise<FindItem[]> {
const all = await getAllFinds();
return all.filter((f) => f.data.source === sourceName).slice(0, limit);
}
+40
View File
@@ -0,0 +1,40 @@
import type { ImageMetadata } from 'astro';
import alcove from '../assets/reviews/alcove/hero.png';
import altTab from '../assets/reviews/alt-tab/hero.jpg';
import cotypist from '../assets/reviews/cotypist/hero.gif';
import fluxMarkdown from '../assets/reviews/flux-markdown/demo.gif';
import folderPeek from '../assets/reviews/folder-peek/screenshot.png';
import hyperkey from '../assets/reviews/hyperkey/hero.png';
import ice from '../assets/reviews/ice/ice-bar.png';
import macMouseFix from '../assets/reviews/mac-mouse-fix/studio-display.png';
import tot from '../assets/reviews/tot/seven-dots.png';
import antinote from '../assets/reviews/antinote/swipe.mp4';
import dockdoor from '../assets/reviews/dockdoor/alttab.mp4';
import homerow from '../assets/reviews/homerow/overlay.mp4';
import monocle from '../assets/reviews/monocle/spotlight.mp4';
import shottr from '../assets/reviews/shottr/text-erase.mp4';
import typewhisper from '../assets/reviews/typewhisper/demo.mp4';
export type Hero =
| { kind: 'image'; src: ImageMetadata }
| { kind: 'video'; src: string };
export const heroes: Record<string, Hero | undefined> = {
alcove: { kind: 'image', src: alcove },
'alt-tab': { kind: 'image', src: altTab },
cotypist: { kind: 'image', src: cotypist },
'flux-markdown': { kind: 'image', src: fluxMarkdown },
'folder-peek': { kind: 'image', src: folderPeek },
hyperkey: { kind: 'image', src: hyperkey },
ice: { kind: 'image', src: ice },
'mac-mouse-fix': { kind: 'image', src: macMouseFix },
tot: { kind: 'image', src: tot },
antinote: { kind: 'video', src: antinote },
dockdoor: { kind: 'video', src: dockdoor },
homerow: { kind: 'video', src: homerow },
monocle: { kind: 'video', src: monocle },
shottr: { kind: 'video', src: shottr },
typewhisper: { kind: 'video', src: typewhisper },
};