Files
unique/src/content.config.ts
T
rzen 7e1b200bea site: editorial tooling, grid masonry fix, new reviews + finds
UI / dev tooling
- /find/ index: sort by file birthtime desc (sub-day chronology even when
  many finds share the same frontmatter date); render the captured-at on
  its own line under the host (~0.65rem mono muted)
- New dev-only filter at the top of /find/: All / Not in lists, persisted
  in localStorage; "not in lists" hides finds referenced by any superpost
- New editorial-tags chip strip on each find card (dev-only): selected
  tags visible by default, full picker on hover/focus-within with a "…"
  button for arbitrary tag entry; clicks copy /edit-find-tag <slug> <tag>
  to the clipboard via the existing EditConfirm singleton
- New optional editorialTags: string[] field on the find collection
- New src/data/editorial-tags.json — master list of available tags (initial
  set: ["delete"]); display order follows file order

/grid/ now uses the JS Masonry component instead of CSS column-count, so
the middle column no longer "dips"; tile hover updated to match the home
page's accent-glow / no-translate style

Content
- New review: apex-markdown-processor (graduated from find of same slug;
  hero reused from the find folder)
- New review: sindre-sorhus-older-mac-apps (graduated; intentionally
  hero-less per the source page's text-only design)
- New review: superkey (added by user; hero wired in src/lib/hero.ts)
- 24 new finds captured by /daily-finds across travel, jokes, quotes,
  product picks, and indie tools
- 2 new finds superposts: 2026-05-04-a-small-atlas, 2026-05-04-off-until-needed

Misc
- daily-finds.log.md and candidate-sources.md updated with the day's runs
- build-finds skill prompt iterated by user
2026-05-04 18:57:04 -04:00

81 lines
2.0 KiB
TypeScript

import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
const stripIndex = ({ entry }: { entry: string }) =>
entry.replace(/\/index\.mdx$/, '');
const reviews = defineCollection({
loader: glob({
pattern: '**/index.mdx',
base: './src/content/reviews',
generateId: stripIndex,
}),
schema: z.object({
name: z.string(),
subtitle: z.string(),
date: z.coerce.date(),
category: z.string(),
tags: z.array(z.string()).default([]),
link: z.string().url().optional(),
linkText: z.string().optional(),
description: z.string().optional(),
source: z.string().optional(),
fromFind: z.string().optional(),
}),
});
const posts = defineCollection({
loader: glob({
pattern: '**/index.mdx',
base: './src/content/posts',
generateId: stripIndex,
}),
schema: z.object({
title: z.string(),
date: z.coerce.date(),
category: z.string(),
tags: z.array(z.string()).default([]),
description: z.string().optional(),
}),
});
const find = defineCollection({
loader: glob({
pattern: '**/index.mdx',
base: './src/content/find',
generateId: stripIndex,
}),
schema: z.object({
name: z.string(),
subtitle: z.string().optional(),
date: z.coerce.date(),
link: z.string().url(),
linkText: z.string().optional(),
source: z.string(),
topics: z.array(z.string()).default([]),
tags: z.array(z.string()).default([]),
description: z.string().optional(),
promotedTo: z.string().optional(),
editorialTags: z.array(z.string()).default([]),
}),
});
const finds = defineCollection({
loader: glob({
pattern: '**/index.mdx',
base: './src/content/finds',
generateId: stripIndex,
}),
schema: z.object({
title: z.string(),
date: z.coerce.date(),
category: z.string().default('finds'),
tags: z.array(z.string()).default([]),
description: z.string().optional(),
blurb: z.string().optional(),
items: z.array(z.string()).min(1),
}),
});
export const collections = { reviews, posts, find, finds };