Add a bounded relation graph and knowledge journeys.

CONTENT-WP-0004 keeps outgoing and incoming labels apart, limits the graph to two hops, and stores focus and detail in the URL.

Assistant: grok
Assistant-Session: 01a1531b-6698-7732-8587-f28ea604bcdf
This commit is contained in:
tegwick 2026-10-19 10:00:20 +02:00
parent 749752df4e
commit b5b6f6bc00
15 changed files with 1290 additions and 52 deletions

View file

@ -0,0 +1,37 @@
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { GraphExplorer } from "@/components/GraphExplorer";
import { findGraph } from "@/lib/catalog";
function first(value: string | string[] | undefined): string | undefined {
return Array.isArray(value) ? value[0] : value;
}
export async function generateMetadata({ params }: { params: Promise<{ dataspace: string }> }): Promise<Metadata> {
const { dataspace } = await params;
const model = findGraph(dataspace);
if (!model) return { title: "Content Explorer" };
return { title: `Graph · ${model.dataspaceTitle} · Content Explorer` };
}
export default async function GraphPage({
params,
searchParams,
}: {
params: Promise<{ dataspace: string }>;
searchParams: Promise<Record<string, string | string[] | undefined>>;
}) {
const { dataspace } = await params;
const query = await searchParams;
const model = findGraph(dataspace);
if (!model) notFound();
const initial = new URLSearchParams();
const focus = first(query.focus);
const detail = first(query.detail);
const hops = first(query.hops);
if (focus) initial.set("focus", focus);
if (detail) initial.set("detail", detail);
if (hops) initial.set("hops", hops);
const initialSearch = initial.size > 0 ? `?${initial.toString()}` : "";
return <GraphExplorer model={model} initialSearch={initialSearch} />;
}