]> 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 7ca6cba..0dbb230 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 1901e2a..259f0a4 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 3b2abe8..c67e8cd 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 e58ce1d..0818b82 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 47c0567..a356e40 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;
   }