diff --git a/docs/99-changelog.md b/docs/99-changelog.md index 14b3c35a..8ac0fbd0 100644 --- a/docs/99-changelog.md +++ b/docs/99-changelog.md @@ -29,8 +29,8 @@ identified with a specific icon: - 🌱 *console*: the user and the theme switch are at the foot of the menu. The bar above the pages is only shown on a small screen, where it opens the menu -- 🩹 *console*: the page for an unknown address says what happened and links - to the home page and to the documentation +- 🩹 *console*: the page for an unknown address or an unknown document says + what happened and links to the home page and to the documentation ## FlowScope 2.0.0-rs.0 - 2026-10-10 diff --git a/web/src/app/docs/page.tsx b/web/src/app/docs/page.tsx index 77e70444..c02b27c3 100644 --- a/web/src/app/docs/page.tsx +++ b/web/src/app/docs/page.tsx @@ -8,8 +8,16 @@ import { useQuery } from "@tanstack/react-query"; import { ChevronRight, CircleAlert, GitBranch } from "lucide-react"; import { SectionLabel } from "@/components/brand"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { NotFound } from "@/components/not-found"; import { useDocumentTitle, useServerConfig } from "@/components/providers"; -import { apiUrl, appPath, appUrl, basePath, type DocumentToc } from "@/lib/api"; +import { + ApiError, + apiUrl, + appPath, + appUrl, + basePath, + type DocumentToc, +} from "@/lib/api"; import { cn } from "@/lib/utils"; import { useScrollFade } from "@/hooks/use-scroll-fade"; @@ -66,7 +74,8 @@ export default function DocumentationPage() { }); const answer = await response.json().catch(() => null); if (!response.ok) { - throw new Error( + throw new ApiError( + response.status, answer?.message ?? `Server returned an error: ${response.status} ${response.statusText}`, ); @@ -87,6 +96,9 @@ export default function DocumentationPage() { [data], ); const toc = data?.toc ?? []; + // An address like /docs/unknown is served by this page: it says itself + // that there is nothing there. + const notFound = error instanceof ApiError && error.status === 404; // Only the current document is expanded when switching. The other ones // are toggled by the user. @@ -116,7 +128,7 @@ export default function DocumentationPage() { // The title comes from the rendered document. setTitle(content.current?.querySelector("h1")?.textContent ?? null); }, [html]); - useDocumentTitle(title); + useDocumentTitle(notFound ? "Not found" : title); // Hand over to the application the links it knows about, instead of // loading the whole page again. @@ -158,6 +170,12 @@ export default function DocumentationPage() { ); + if (notFound) { + return ( + navigate("intro", "")} /> + ); + } + return (