Files
andwati.com/src/lib/books.ts
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

45 lines
975 B
TypeScript

import {
type Book,
coverExists,
formatDate,
getBooks,
spineFor,
} from "@/lib/content";
export type ShelfBook = {
slug: string;
href: string;
title: string;
coverImage?: string;
spineColor: string;
textColor: string;
};
export function getShelf(): ShelfBook[] {
return getBooks().map((b) => ({
slug: b.slug,
href: `/reading/${b.slug}`,
title: b.data.title,
coverImage: coverExists(b.data.coverImage) ? b.data.coverImage : undefined,
...spineFor(b.data.title),
}));
}
export function readLine(b: Book) {
return [
b.dateFinished && `Read: ${formatDate(b.dateFinished)}`,
b.rating && `Rating: ${b.rating}/5`,
]
.filter(Boolean)
.join(" • ");
}
export function byline(b: Book) {
const parts = [
b.authors?.length && `By: ${b.authors.join(", ")}`,
b.dateFinished && `Read: ${formatDate(b.dateFinished)}`,
b.rating && `Rating: ${b.rating}/5`,
];
return parts.filter(Boolean).join(" - ");
}