Start the rebuild: note schema, vault kit and data migration

Phase 1 of the functional design plan.

- Move the old site into legacy/, unchanged, for reference. It still
  builds from there until cutover.
- Add packages/schema: the one definition of all 20 note types, with
  field titles and descriptions, note and vault checks, and JSON Schema
  export so later editing tools and the MCP server share it.
- Add vault-kit: Obsidian templates generated from the schema, and the
  Site guide for 24.01_PersonalWebsite.
- Add tools/migrate: turns the legacy JSON, collections, store data and
  page text into 545 Site folder notes and 84 images, and checks the
  result with the schema.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
This commit is contained in:
Claude
2026-10-11 18:40:55 +00:00
parent 4ded4b277b
commit a2f723ca2d
324 changed files with 11538 additions and 7556 deletions
+118
View File
@@ -0,0 +1,118 @@
---
/**
* anime.astro — Anime
* ---------------------------------------------------------------------------
* The primary pillar: 150+ completed series. Favorites render as poster cards
* up top (populated by scripts/fetch-anime-art.mjs); the full library is
* grouped by category below. Follows the canonical interest-page template.
*/
import BaseLayout from "@layouts/BaseLayout.astro";
import Reveal from "@components/motion/Reveal.astro";
import InterestHero from "@components/layout/InterestHero.astro";
import MediaCard from "@components/cards/MediaCard.astro";
import CategoryList from "@components/media/CategoryList.astro";
import SectionHeader from "@components/layout/SectionHeader.astro";
import RelatedInterests from "@components/layout/RelatedInterests.astro";
import Placeholder from "@components/layout/Placeholder.astro";
import { getFavorites, getGrouped, mediaKinds, getCount } from "@lib/media";
const meta = mediaKinds.anime;
const favorites = getFavorites("anime");
const groups = getGrouped("anime");
const count = getCount("anime");
const related = [
{
href: "/interests/tv",
title: "TV Shows",
icon: "television-classic",
blurb: "When I step away from animation, the live-action backlog is waiting.",
},
{
href: "/interests/reading",
title: "Reading",
icon: "book-open-page-variant",
blurb: "Half of these have light novels and manga I'm reading too.",
},
];
---
<BaseLayout
surface="interest"
title={meta.title}
description={`${count}+ completed anime series. Shonen, isekai, dark psychological. The primary pillar of my watchlist, not a side shelf.`}
>
<InterestHero
icon={meta.icon}
title={meta.title}
tagline={meta.tagline}
image={meta.heroImage}
imageAlt={meta.heroImageAlt}
/>
<div class="container-prose flex flex-col gap-20 py-12 sm:py-16">
{/* Overview */}
<section
class="flex flex-col gap-5 text-lg leading-relaxed"
style="color: var(--color-text-muted);"
>
<Reveal><p>{meta.overview}</p></Reveal>
</section>
{/* Favorites */}
<section class="flex flex-col gap-6">
<Reveal
><SectionHeader
kicker="The rewatch pile"
icon="star"
title="Favorites"
/></Reveal
>
{
favorites.length > 0 ? (
<div class="grid gap-5 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{favorites.map((item, i) => (
<Reveal delay={i * 70}>
<MediaCard
kind="anime"
title={item.title}
image={item.image}
status={item.status}
blurb={item.blurb}
/>
</Reveal>
))}
</div>
) : (
<Placeholder
label="Anime favorites"
hint='Mark items with "favorite": true in src/data/media/anime.json.'
/>
)
}
</section>
{/* Full library */}
<section class="flex flex-col gap-6">
<Reveal
><SectionHeader
kicker={`${count}+ and counting`}
icon="format-list-bulleted"
title="The full watch history"
/></Reveal
>
<Reveal delay={80}>
{groups.length > 0 ? (
<CategoryList groups={groups} pillIcon="play-circle" />
) : (
<Placeholder
label="Anime library"
hint="Add categorized items to src/data/media/anime.json."
/>
)}
</Reveal>
</section>
<RelatedInterests items={related} />
</div>
</BaseLayout>