Files
andwati.com/src/app/reading/page.tsx
T
andwati ec6401ac38 Replace Astro/Strapi site with Next.js static export
- 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
2026-09-30 12:48:14 +03:00

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>
);
}