mirror of
https://github.com/andwati/andwati.com.git
synced 2026-10-25 20:32:16 +00:00
- Fresh structure: Next.js App Router + Tailwind v4 at the repo root, exported as static files (output: export) and served by static-web-server - Remove the Strapi CMS, Astro app, Pagefind search, comments, old scripts, Lighthouse config and compose files; content in content/ is now plain markdown with YAML frontmatter - Pages: About, Writing, Reading (3D bookshelf) and Portfolio, styled after adammaj.com - SEO: metadata and canonicals, JSON-LD, generated OG images, sitemap, robots, RSS and JSON feeds, llms.txt - Umami via PUBLIC_UMAMI_SCRIPT_URL / PUBLIC_UMAMI_WEBSITE_ID (build args) - Dockerfile builds the export and serves it with sws.toml (security headers, clean URLs, .well-known, OG image content type, immutable asset caching) - Prettier and cspell for content with a husky/lint-staged pre-commit hook - Old redirect list dropped to start from a clean slate
71 lines
2.5 KiB
TypeScript
71 lines
2.5 KiB
TypeScript
import type { Metadata } from "next";
|
|
import Link from "next/link";
|
|
import { Mdx } from "@/components/Mdx";
|
|
import { readLine } from "@/lib/books";
|
|
import { coverExists, getBooks, spineFor, summarize } from "@/lib/content";
|
|
|
|
export const metadata: Metadata = {
|
|
title: "Reading",
|
|
description: "Books and papers I have read, with ratings and notes.",
|
|
alternates: { canonical: "/reading" },
|
|
};
|
|
|
|
export default function ReadingIndex() {
|
|
return (
|
|
<div className="flex flex-col">
|
|
<h1 className="sr-only">Reading</h1>
|
|
{getBooks().map((b, i) => {
|
|
const cover = coverExists(b.data.coverImage)
|
|
? b.data.coverImage
|
|
: undefined;
|
|
const { spineColor, textColor } = spineFor(b.data.title);
|
|
const summary = summarize(b.content);
|
|
return (
|
|
<div key={b.slug} className={i > 0 ? "mt-5" : undefined}>
|
|
{i > 0 && <hr className="mb-3" />}
|
|
<div className="flex gap-6">
|
|
{cover ? (
|
|
// biome-ignore lint/performance/noImgElement: small local cover
|
|
<img
|
|
src={cover}
|
|
alt={b.data.title}
|
|
className="h-[100px] border border-gray-200 sm:h-[140px] md:h-[160px]"
|
|
style={{ width: "auto" }}
|
|
/>
|
|
) : (
|
|
<div
|
|
className="flex h-[100px] w-[68px] shrink-0 items-center border border-gray-200 p-2 text-xs font-bold sm:h-[140px] sm:w-24 md:h-[160px] md:w-28"
|
|
style={{ backgroundColor: spineColor, color: textColor }}
|
|
>
|
|
{b.data.title}
|
|
</div>
|
|
)}
|
|
<div className="flex min-w-0 flex-col">
|
|
<Link
|
|
href={`/reading/${b.slug}`}
|
|
className="block hover:underline"
|
|
>
|
|
<h2 className="text-[20px] leading-6 font-bold">
|
|
{b.data.title}
|
|
</h2>
|
|
</Link>
|
|
{b.data.authors?.length ? (
|
|
<p className="mt-2 text-[#999]">
|
|
{b.data.authors.join(", ")}
|
|
</p>
|
|
) : null}
|
|
<p className="mt-2 text-[#666]">{readLine(b.data)}</p>
|
|
{summary && (
|
|
<article className="prose mt-2">
|
|
<Mdx source={summary} />
|
|
</article>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|