Files
wehub-resource-sync e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:40:13 +08:00

67 lines
2.0 KiB
TypeScript

import type { Node, Root } from "fumadocs-core/page-tree";
import type { ReactNode } from "react";
export type DocsNeighbour = {
name: ReactNode;
url: string;
section?: ReactNode;
};
type Section = { id: string; name: ReactNode } | undefined;
type FlatPage = { name: ReactNode; url: string; section: Section };
/* fumadocs findNeighbour stops at root:true folder boundaries, so the last page of a section has no next. Flatten every section into one ordered list so navigation continues across sections, tagging each page with the section it belongs to. */
function flatten(tree: Root): FlatPage[] {
const pages: FlatPage[] = [];
let rootCount = 0;
const walk = (nodes: Node[], section: Section) => {
for (const node of nodes) {
if (node.type === "page") {
pages.push({ name: node.name, url: node.url, section });
} else if (node.type === "folder") {
const childSection: Section = node.root
? { id: `root-${rootCount++}`, name: node.name }
: section;
if (node.index) {
pages.push({
name: node.index.name,
url: node.index.url,
section: childSection,
});
}
walk(node.children, childSection);
}
}
};
walk(tree.children, undefined);
return pages;
}
export function getDocsNeighbours(
tree: Root,
url: string,
): { previous?: DocsNeighbour | undefined; next?: DocsNeighbour | undefined } {
const pages = flatten(tree);
const index = pages.findIndex((page) => page.url === url);
if (index === -1) return {};
const current = pages[index]!;
const toNeighbour = (
page: FlatPage | undefined,
): DocsNeighbour | undefined => {
if (!page) return undefined;
const crossesSection =
page.section != null && page.section.id !== current.section?.id;
return {
name: page.name,
url: page.url,
...(crossesSection ? { section: page.section!.name } : {}),
};
};
return {
previous: toNeighbour(pages[index - 1]),
next: toNeighbour(pages[index + 1]),
};
}