From 439d138967dc8d0d61b684c5dc1bc8cd3a24f72a Mon Sep 17 00:00:00 2001 From: Lucas Winther Date: Fri, 30 Oct 2026 21:37:56 +0100 Subject: [PATCH] feat(web): app shell, library, entry detail Co-Authored-By: Claude Opus 5.5 --- apps/web/src/components/Cover.tsx | 18 + apps/web/src/components/DetailBoxes.tsx | 520 ++++++++++++++++++++ apps/web/src/components/EntryDetail.tsx | 189 +++++++ apps/web/src/components/HistoryDrawer.tsx | 115 +++++ apps/web/src/components/Icon.tsx | 21 + apps/web/src/components/LinkToCatalogue.tsx | 102 ++++ apps/web/src/components/Shell.tsx | 75 +++ apps/web/src/components/StarRating.tsx | 148 ++++++ apps/web/src/hooks.ts | 46 ++ apps/web/src/main.tsx | 73 +++ apps/web/src/routes/library.tsx | 228 +++++++++ apps/web/src/routes/login.tsx | 51 ++ apps/web/src/routes/not-yet.tsx | 24 + apps/web/src/routes/register.tsx | 92 ++++ apps/web/src/settings.ts | 38 ++ apps/web/test/api.test.ts | 6 +- 16 files changed, 1743 insertions(+), 3 deletions(-) create mode 100644 apps/web/src/components/Cover.tsx create mode 100644 apps/web/src/components/DetailBoxes.tsx create mode 100644 apps/web/src/components/EntryDetail.tsx create mode 100644 apps/web/src/components/HistoryDrawer.tsx create mode 100644 apps/web/src/components/Icon.tsx create mode 100644 apps/web/src/components/LinkToCatalogue.tsx create mode 100644 apps/web/src/components/Shell.tsx create mode 100644 apps/web/src/components/StarRating.tsx create mode 100644 apps/web/src/hooks.ts create mode 100644 apps/web/src/main.tsx create mode 100644 apps/web/src/routes/library.tsx create mode 100644 apps/web/src/routes/login.tsx create mode 100644 apps/web/src/routes/not-yet.tsx create mode 100644 apps/web/src/routes/register.tsx create mode 100644 apps/web/src/settings.ts diff --git a/apps/web/src/components/Cover.tsx b/apps/web/src/components/Cover.tsx new file mode 100644 index 0000000..f477423 --- /dev/null +++ b/apps/web/src/components/Cover.tsx @@ -0,0 +1,18 @@ +import { useState } from "react"; +import { mediaColour } from "../format.ts"; + +/** A cover image, the media-type gradient when there is none, or the striped draft placeholder. */ +export function Cover({ url, mediaType, draft, size }: { url: string; mediaType: string; draft: boolean; size?: "large" | "small" }) { + const [failed, setFailed] = useState(false); + const cls = `cover${size ? ` ${size}` : ""}`; + if (url && !failed) { + return ( +
+ setFailed(true)} /> +
+ ); + } + if (draft) return