]> git.otsuka.systems Git - cotsuka.github.io/commitdiff
add publication metadata and image to frontmatter
authorCameron Otsuka <cameron@otsuka.haus>
Fri, 1 May 2026 00:00:54 +0000 (17:00 -0700)
committerCameron Otsuka <cameron@otsuka.haus>
Fri, 1 May 2026 00:00:54 +0000 (17:00 -0700)
src/components/metadata.astro
src/components/ui/contentlist.astro
src/content.config.ts
src/pages/feed.xml.ts
src/pages/search.astro

index 7ca6cba9e51d9b9c06c158eb495963f0c37e9f5f..0dbb230d0df7536b65b79a9c02be124ceb7e637c 100644 (file)
@@ -18,6 +18,17 @@ const modifiedDate = entryData.modified
   <summary>Metadata</summary>
   <ul>
     {entryData.description && <li>Description: {entryData.description}</li>}
+    {
+      'publication' in entryData && entryData.publication && (
+        <li
+          data-pagefind-meta={`publication:${entryData.publication.name}`}
+          data-pagefind-filter={`publication:${entryData.publication.name}`}
+        >
+          Publication: {entryData.publication.name}{' '}
+          {entryData.publication.issue}-{entryData.publication.volume}
+        </li>
+      )
+    }
     {
       'rating' in entryData && entryData.rating && (
         <li>Rating: {generateStarRating(entryData.rating)}</li>
@@ -35,7 +46,7 @@ const modifiedDate = entryData.modified
     >
       Type: {entryData.type}
     </li>
-    {entryData.tags && <li>Tags: {entryData.tags.join(', ')}</li>}
+    {entryData.tags.length > 0 && <li>Tags: {entryData.tags.join(', ')}</li>}
     {
       entryData.posse && (
         <li>
index 1901e2a27e21c34d6383b418622e30445b75a36b..259f0a47ba4fd30aa5fab02715fbd75b0b29ed4e 100644 (file)
@@ -27,7 +27,9 @@ const displayedEntries =
         </dt>
         <dd>
           <Badge>{entry.data.type}</Badge>
-          {entry.data.description && entry.data.description}
+          {'publication' in entry.data && entry.data.publication
+            ? `${entry.data.publication.name} ${entry.data.publication.issue}-${entry.data.publication.volume}`
+            : entry.data.description}
         </dd>
       </>
     ))
index 3b2abe8d5791d9dfa912998b46db1d2038571879..c67e8cd4d1fe8907287d244881b3e757375aea7f 100644 (file)
@@ -1,43 +1,56 @@
-import { defineCollection } from 'astro:content';
+import { defineCollection, type SchemaContext } from 'astro:content';
 import { glob } from 'astro/loaders';
 import { z } from 'astro/zod';
 
-const baseSchema = z.object({
-  title: z.string(),
-  description: z.string().optional(),
-  date: z.coerce.date(),
-  modified: z.coerce.date().optional(),
-  tags: z.array(z.string()).optional(),
-  posse: z.record(z.string(), z.url()).optional(),
+const baseSchema = (image: SchemaContext['image']) =>
+  z.object({
+    title: z.string(),
+    description: z.string().optional(),
+    date: z.coerce.date(),
+    modified: z.coerce.date().optional(),
+    tags: z.array(z.string()).default([]),
+    posse: z.record(z.string(), z.url()).optional(),
+    image: image().optional(),
+  });
+
+const publication = z.object({
+  name: z.enum(['Build Weekly Roundup', 'Mine Print Hash', 'First Pass']),
+  issue: z.coerce.number().positive(),
+  volume: z.coerce.number().positive(),
 });
 
 const articles = defineCollection({
   loader: glob({ pattern: '**/*.{md,mdx}', base: 'content/articles' }),
-  schema: baseSchema.extend({
-    type: z.enum(['note', 'essay']),
-  }),
+  schema: ({ image }) =>
+    baseSchema(image).extend({
+      type: z.enum(['essay', 'newsletter', 'note']),
+      publication: publication.optional(),
+    }),
 });
 
 const podcasts = defineCollection({
   loader: glob({ pattern: '**/*.{md,mdx}', base: 'content/podcasts' }),
-  schema: baseSchema.extend({
-    type: z.enum(['audio', 'video']),
-    enclosure: z
-      .object({
-        url: z.url(),
-        length: z.number(),
-        type: z.string(),
-      })
-      .optional(),
-  }),
+  schema: ({ image }) =>
+    baseSchema(image).extend({
+      type: z.enum(['audio', 'video']),
+      publication: publication.optional(),
+      enclosure: z
+        .object({
+          url: z.url(),
+          length: z.coerce.number().int().nonnegative(),
+          type: z.string(),
+        })
+        .optional(),
+    }),
 });
 
 const reviews = defineCollection({
   loader: glob({ pattern: '**/*.{md,mdx}', base: 'content/reviews' }),
-  schema: baseSchema.extend({
-    rating: z.int().gt(0).lte(5),
-    type: z.enum(['book', 'game', 'movie', 'music', 'show']),
-  }),
+  schema: ({ image }) =>
+    baseSchema(image).extend({
+      rating: z.int().gt(0).lte(5),
+      type: z.enum(['book', 'game', 'movie', 'music', 'show']),
+    }),
 });
 
 export const collections = { articles, podcasts, reviews };
index e58ce1df1b05d8b3db0dc4992d1fec93b931c3af..0818b829cfbf9c390b32de5d9402bdd2069ac7f0 100644 (file)
@@ -28,8 +28,11 @@ export async function GET(context: APIContext) {
         const link = generateContentUrl(item);
         const { Content } = await render(item);
         const content = await container.renderToString(Content);
-        const categories = (item.data.tags ?? []).concat(item.collection);
+        const categories = item.data.tags.concat(item.collection);
         categories.push(item.data.type);
+        if ('publication' in item.data && item.data.publication) {
+          categories.push(item.data.publication.name);
+        }
 
         let title: string;
         let description: string;
index 47c0567351dd592b81d634e550079881e270c8cc..a356e408db7970cac0d4fc910f2df7cdda7a5843 100644 (file)
@@ -29,6 +29,8 @@ const pagefindResultTemplate = String.raw`
     <div class="searchFilters" aria-label="Search result filters">
       <pagefind-filter-dropdown filter="category" label="Category"
       ></pagefind-filter-dropdown>
+      <pagefind-filter-dropdown filter="publication" label="Publication"
+      ></pagefind-filter-dropdown>
       <pagefind-filter-dropdown filter="type" label="Type"
       ></pagefind-filter-dropdown>
       <pagefind-filter-dropdown
@@ -77,7 +79,7 @@ const pagefindResultTemplate = String.raw`
 
   .searchFilters {
     display: grid;
-    grid-template-columns: repeat(3, minmax(0, 1fr));
+    grid-template-columns: repeat(2, minmax(0, 1fr));
     gap: 1rem;
     margin-block: 1rem;
   }