Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/99-changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
24 changes: 21 additions & 3 deletions web/src/app/docs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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}`,
);
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -158,6 +170,12 @@ export default function DocumentationPage() {
</a>
);

if (notFound) {
return (
<NotFound what="document" onDocumentation={() => navigate("intro", "")} />
);
}

return (
<div className="flex size-full flex-col md:flex-row">
<nav
Expand Down
27 changes: 3 additions & 24 deletions web/src/app/not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,12 @@
// SPDX-License-Identifier: AGPL-3.0-only

import type { Metadata } from "next";
import Link from "next/link";
import { ErrorPage } from "@/components/error-page";
import { buttonVariants } from "@/components/ui/button";
import { NotFound } from "@/components/not-found";

export const metadata: Metadata = {
title: "FlowScope | Not found",
};

export default function NotFound() {
return (
<ErrorPage
code="Error 404"
error="This page does not exist"
description="The address may be mistyped, or the page has moved."
>
<Link href="/" className={buttonVariants({ className: "h-11 px-5" })}>
Back to home
</Link>
<Link
href="/docs"
className={buttonVariants({
variant: "outline",
className: "h-11 bg-card px-5",
})}
>
Open the documentation
</Link>
</ErrorPage>
);
export default function NotFoundPage() {
return <NotFound />;
}
48 changes: 48 additions & 0 deletions web/src/components/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2026 FlowScope contributors
// SPDX-License-Identifier: AGPL-3.0-only

"use client";

import Link from "next/link";
import { ErrorPage } from "@/components/error-page";
import { buttonVariants } from "@/components/ui/button";

/**
* What to show for an address which leads nowhere, with two ways out. The
* documentation follows its own links: it gives `onDocumentation`.
*/
export function NotFound({
what = "page",
onDocumentation,
}: {
what?: "page" | "document";
onDocumentation?: () => void;
}) {
return (
<ErrorPage
code="Error 404"
error={`This ${what} does not exist`}
description={`The address may be mistyped, or the ${what} has moved.`}
>
<Link href="/" className={buttonVariants({ className: "h-11 px-5" })}>
Back to home
</Link>
<Link
href="/docs"
onClick={
onDocumentation &&
((event) => {
event.preventDefault();
onDocumentation();
})
}
className={buttonVariants({
variant: "outline",
className: "h-11 bg-card px-5",
})}
>
Open the documentation
</Link>
</ErrorPage>
);
}
Loading