mirror of
https://github.com/fosrl/docs-v2.git
synced 2026-09-27 00:09:07 +02:00
93 lines
2.5 KiB
TypeScript
93 lines
2.5 KiB
TypeScript
import type * as PageTree from 'fumadocs-core/page-tree';
|
|
import { Icon } from '@/components/icons';
|
|
import { source } from './source';
|
|
import navigation from './navigation.json';
|
|
|
|
/**
|
|
* The sidebar is defined in `lib/navigation.json` (same shape as Mintlify's `docs.json`
|
|
* groups), because the groups do not map 1:1 to folders on disk. Pages that exist in
|
|
* `content/docs` but are not listed here are still published, just hidden from the sidebar.
|
|
*/
|
|
type NavItem = string | NavGroup;
|
|
interface NavGroup {
|
|
group: string;
|
|
icon?: string;
|
|
pages: NavItem[];
|
|
}
|
|
|
|
export interface NavLink {
|
|
label: string;
|
|
href: string;
|
|
icon?: string | null;
|
|
}
|
|
|
|
export const navTabs: NavLink[] = navigation.tabs;
|
|
export const navAnchors: NavLink[] = navigation.anchors;
|
|
|
|
function pageNode(path: string): PageTree.Item | null {
|
|
const slugs = path === 'index' ? [] : path.split('/');
|
|
const page = source.getPage(slugs);
|
|
if (!page) {
|
|
console.warn(`[navigation] page not found: ${path}`);
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
$id: `page:${path}`,
|
|
type: 'page',
|
|
name: page.data.title,
|
|
url: page.url,
|
|
icon: page.data.icon ? <Icon name={page.data.icon} /> : undefined,
|
|
};
|
|
}
|
|
|
|
function buildItems(items: NavItem[], idPrefix: string): PageTree.Node[] {
|
|
const out: PageTree.Node[] = [];
|
|
for (const item of items) {
|
|
if (typeof item === 'string') {
|
|
const node = pageNode(item);
|
|
if (node) out.push(node);
|
|
continue;
|
|
}
|
|
|
|
const id = `${idPrefix}/${item.group}`;
|
|
out.push({
|
|
$id: id,
|
|
type: 'folder',
|
|
name: item.group,
|
|
icon: item.icon ? <Icon name={item.icon} /> : undefined,
|
|
children: buildItems(item.pages, id),
|
|
});
|
|
}
|
|
return out;
|
|
}
|
|
|
|
let cached: PageTree.Root | undefined;
|
|
|
|
export function getNavigationTree(): PageTree.Root {
|
|
if (cached) return cached;
|
|
|
|
const children: PageTree.Node[] = [];
|
|
for (const group of navigation.groups as NavGroup[]) {
|
|
// top-level groups render as sidebar section headings
|
|
children.push({ $id: `sep:${group.group}`, type: 'separator', name: group.group });
|
|
children.push(...buildItems(group.pages, group.group));
|
|
}
|
|
|
|
cached = { $id: 'root', name: 'Docs', children };
|
|
return cached;
|
|
}
|
|
|
|
/** Page paths in sidebar order (first occurrence wins), used for llms-full.txt */
|
|
export function getOrderedPagePaths(): string[] {
|
|
const seen = new Set<string>();
|
|
function walk(items: NavItem[]) {
|
|
for (const item of items) {
|
|
if (typeof item === 'string') seen.add(item);
|
|
else walk(item.pages);
|
|
}
|
|
}
|
|
walk(navigation.groups as NavGroup[]);
|
|
return [...seen];
|
|
}
|