site: rename "finds" superpost collection to "bundles"

Disambiguates the one-letter `find` (item) vs `finds` (roundup) split that
kept causing confusion. Audience-facing now: `/bundles/<slug>/`, "Bundle"
labels in home/footer/search.

Internals: `getCollection('finds')` → `('bundles')`, `Entry.type 'finds'`
→ `'bundle'`, `OgFallback 'finds'` → `'bundles'`, `findListMosaic` →
`bundleMosaic`, `FindsPost` → `Bundle`, `findsReferencing` →
`bundlesReferencing`. Build script + generated mosaic dir follow
(`scripts/build-bundle-mosaics.mjs`, `src/generated/bundle-mosaics/`).

Skills `build-finds` → `build-bundle`, `cluster-finds` →
`cluster-bundles`. The `find` collection (individual items) is unchanged.

No URL redirects: low external-link surface area.
This commit is contained in:
2026-05-06 20:43:22 -04:00
parent c2e1df222d
commit 049c6c5377
36 changed files with 503 additions and 255 deletions
+20 -20
View File
@@ -4,10 +4,10 @@ import { Image } from 'astro:assets';
import BaseLayout from '../layouts/BaseLayout.astro';
import Masonry from '../components/Masonry.astro';
import { heroes } from '../lib/hero';
import { findListMosaic } from '../lib/find-list-mosaic';
import { bundleMosaic } from '../lib/bundle-mosaic';
import { buildOgImage, buildItemList, WEBSITE, ORGANIZATION } from '../lib/seo';
const HERO_SLUG = 'monitorcontrol';
const HERO_SLUG = 'gum-shell';
const INITIAL_GRID = 12;
const BATCH_SIZE = 7;
@@ -22,23 +22,23 @@ type Tile =
hero: ReturnType<typeof getReviewHero>;
}
| {
kind: 'list';
kind: 'bundle';
href: string;
id: string;
name: string;
subtitle: string | undefined;
date: Date;
itemCount: number;
mosaic: ReturnType<typeof findListMosaic>;
mosaic: ReturnType<typeof bundleMosaic>;
};
function getReviewHero(slug: string) {
return heroes[slug];
}
const [reviews, lists] = await Promise.all([
const [reviews, bundles] = await Promise.all([
getCollection('reviews'),
getCollection('finds'),
getCollection('bundles'),
]);
const reviewTiles: Tile[] = reviews.map((review) => ({
@@ -51,18 +51,18 @@ const reviewTiles: Tile[] = reviews.map((review) => ({
hero: getReviewHero(review.id),
}));
const listTiles: Tile[] = lists.map((list) => ({
kind: 'list' as const,
href: `/finds/${list.id}/`,
id: list.id,
name: list.data.title,
subtitle: list.data.blurb,
date: list.data.date,
itemCount: list.data.items.length,
mosaic: findListMosaic(list.id),
const bundleTiles: Tile[] = bundles.map((bundle) => ({
kind: 'bundle' as const,
href: `/bundles/${bundle.id}/`,
id: bundle.id,
name: bundle.data.title,
subtitle: bundle.data.blurb,
date: bundle.data.date,
itemCount: bundle.data.items.length,
mosaic: bundleMosaic(bundle.id),
}));
const tiles: Tile[] = [...reviewTiles, ...listTiles].sort(
const tiles: Tile[] = [...reviewTiles, ...bundleTiles].sort(
(a, b) => b.date.valueOf() - a.date.valueOf()
);
@@ -130,7 +130,7 @@ const itemList = buildItemList(
tile.kind === 'review' ? tile.hero : tile.mosaic ? { kind: 'image' as const, src: tile.mosaic } : undefined;
return (
<article>
<a href={tile.href} class:list={['tile', { 'text-only': !visual, 'is-list': tile.kind === 'list' }]}>
<a href={tile.href} class:list={['tile', { 'text-only': !visual, 'is-bundle': tile.kind === 'bundle' }]}>
{visual?.kind === 'image' && (
<Image
class="media"
@@ -155,8 +155,8 @@ const itemList = buildItemList(
/>
)}
<div class="text">
{tile.kind === 'list' && (
<span class="eyebrow">List · {tile.itemCount} picks</span>
{tile.kind === 'bundle' && (
<span class="eyebrow">Bundle · {tile.itemCount} picks</span>
)}
<span class="name">{tile.name}</span>
{tile.subtitle && <span class="subtitle">{tile.subtitle}</span>}
@@ -260,7 +260,7 @@ const itemList = buildItemList(
color: var(--accent);
font-weight: 600;
}
.tile.is-list .name {
.tile.is-bundle .name {
font-family: ui-serif, Georgia, 'Iowan Old Style', 'Apple Garamond', 'Palatino Linotype', serif;
font-weight: 700;
font-size: 1.15rem;