site: media + colocation — chunk 1/4 (code, configs, docs)

Code, configs, README, CHANGELOG, sources/log files, .claude.
Subsequent chunks land the renamed media files.
This commit is contained in:
2026-05-04 17:54:14 -04:00
parent e873460465
commit 5dcd99361f
25 changed files with 1138 additions and 243 deletions
+47 -29
View File
@@ -1,9 +1,12 @@
---
import { getCollection, render } from 'astro:content';
import { Image } from 'astro:assets';
import BaseLayout from '../../layouts/BaseLayout.astro';
import SourceBadge from '../../components/SourceBadge.astro';
import Backlinks from '../../components/Backlinks.astro';
import { findsReferencing } from '../../lib/backlinks';
import { findHero } from '../../lib/find-hero';
import EditAction from '../../components/EditAction.astro';
import {
resolveExternalLink,
AFFILIATE_REL,
@@ -22,12 +25,7 @@ export async function getStaticPaths() {
const { item } = Astro.props;
const { Content } = await render(item);
const featuringPosts = await findsReferencing(item.id);
const fmt = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
const hero = findHero(item.id);
const linkLabel =
item.data.linkText ?? new URL(item.data.link).hostname.replace(/^www\./, '');
@@ -72,6 +70,10 @@ const breadcrumbs = buildBreadcrumbs([
<BaseLayout
title={item.data.name}
description={description}
subheader={item.data.name}
subheaderSubtitle={item.data.subtitle}
subheaderVariant="headline"
lastUpdated={item.data.date}
ogType="article"
ogImage={ogImage}
canonicalOverride={canonicalOverride}
@@ -80,18 +82,22 @@ const breadcrumbs = buildBreadcrumbs([
jsonLd={[productSchema, breadcrumbs]}
>
<article>
<p class="source-line"><SourceBadge source={item.data.source} /></p>
<header class="find-header">
<h1>{item.data.name}</h1>
{item.data.subtitle && <p class="subtitle">{item.data.subtitle}</p>}
<p class="meta">
<time datetime={item.data.date.toISOString()}>
{fmt.format(item.data.date)}
</time>
</p>
<p class="source-line"><SourceBadge source={item.data.source} /></p>
</header>
{hero && (
<Image
class="hero"
src={hero}
widths={[640, 960, 1280]}
sizes="(max-width: 720px) 92vw, 42rem"
loading="eager"
decoding="async"
alt={item.data.name}
/>
)}
<div class="body">
<Content />
</div>
@@ -120,6 +126,16 @@ const breadcrumbs = buildBreadcrumbs([
)
}
{import.meta.env.DEV && !item.data.promotedTo && (
<div class="edit-toolbar">
<EditAction
label="Graduate to review"
command={`/build-review ${item.id}`}
confirm={`This will draft src/content/reviews/${item.id}.mdx, expand the find body into a full review, propose a hero asset, link the find via promotedTo, and append a graduation note to daily-finds.log.md.`}
/>
</div>
)}
<Backlinks posts={featuringPosts} />
{
@@ -136,23 +152,20 @@ const breadcrumbs = buildBreadcrumbs([
</article>
<style>
.source-line { margin: 0 0 0.5rem; }
.find-header {
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--rule);
}
h1 { margin: 0 0 0.35rem; }
.subtitle {
margin: 0 0 0.75rem;
color: var(--muted);
font-size: 1.1rem;
font-style: italic;
}
.meta {
margin: 0;
color: var(--muted);
font-size: 0.9rem;
.source-line { margin: 0; }
.hero {
display: block;
width: 100%;
height: auto;
max-height: 28rem;
object-fit: cover;
border-radius: 8px;
border: 1px solid var(--rule);
background: var(--card-fill);
margin: 0 0 1.5rem;
}
.body :global(p) { margin: 0 0 1rem; }
.external {
@@ -208,5 +221,10 @@ const breadcrumbs = buildBreadcrumbs([
.tag { font-size: 0.85rem; color: var(--muted); }
.tag:hover { color: var(--accent); }
.back { margin-top: 2rem; font-size: 0.9rem; }
.edit-toolbar {
margin: 2rem 0 0;
padding-top: 1rem;
border-top: 1px dashed color-mix(in srgb, var(--accent) 30%, var(--rule));
}
</style>
</BaseLayout>