Skip to content
Open
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
23 changes: 16 additions & 7 deletions src/layouts/DocsLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import CopyPageDropdown from '../components/CopyPageDropdown.astro';
import PageFeedback from '../components/PageFeedback.tsx';
import GiscusComments from '../components/GiscusComments.tsx';
import FastNav from '../components/FastNav.astro';
import { getFirstChildHrefForPath } from '../lib/navigation';

interface Props {
frontmatter: {
Expand Down Expand Up @@ -57,11 +58,19 @@ function breadcrumbHasPage(segments: string[], upTo: number): boolean {
);
}

const breadcrumbs = pathSegments.map((segment, i) => ({
name: segment.replace(/-/g, ' ').replace(/\b\w/g, (c: string) => c.toUpperCase()),
url: `https://docs.futureagi.com/${pathSegments.slice(0, i + 1).join('/')}`,
hasPage: breadcrumbHasPage(pathSegments, i),
}));
const breadcrumbs = pathSegments.map((segment, i) => {
const relPath = '/' + pathSegments.slice(0, i + 1).join('/');
const hasPage = breadcrumbHasPage(pathSegments, i);
// If no page exists at this URL but children exist in nav, fall back to the
// first child so the breadcrumb segment stays clickable.
const fallbackHref = hasPage ? undefined : getFirstChildHrefForPath(relPath);
return {
name: segment.replace(/-/g, ' ').replace(/\b\w/g, (c: string) => c.toUpperCase()),
url: `https://docs.futureagi.com${relPath}`,
linkHref: hasPage ? relPath : fallbackHref,
hasLink: hasPage || Boolean(fallbackHref),
};
});
// Override last breadcrumb with actual page title
if (breadcrumbs.length > 0) {
breadcrumbs[breadcrumbs.length - 1].name = frontmatter.title;
Expand Down Expand Up @@ -98,8 +107,8 @@ if (breadcrumbs.length > 0) {
<li class="flex items-center gap-1.5">
{i > 0 && <span class="text-[var(--color-text-muted)]">/</span>}
{i < breadcrumbs.length - 1 ? (
crumb.hasPage ? (
<a href={crumb.url.replace('https://docs.futureagi.com', '')} class="hover:text-[var(--color-text-primary)] transition-colors">{crumb.name}</a>
crumb.hasLink && crumb.linkHref ? (
<a href={crumb.linkHref} class="hover:text-[var(--color-text-primary)] transition-colors">{crumb.name}</a>
) : (
<span>{crumb.name}</span>
)
Expand Down
28 changes: 28 additions & 0 deletions src/lib/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,34 @@ function matchesPath(items: NavItem[], normalizedPath: string): boolean {
return false;
}

// Walk nav items and return the first href found that starts with `prefix + '/'`.
// Used to make breadcrumb segments clickable when no page exists at the
// intermediate URL (e.g., `/docs/quickstart` has no page but child pages exist
// under `/docs/quickstart/*`).
function findFirstHrefUnder(items: NavItem[], prefix: string): string | undefined {
for (const item of items) {
if (item.href && item.href !== prefix && item.href.startsWith(prefix + '/')) {
return item.href;
}
if (item.items) {
const found = findFirstHrefUnder(item.items, prefix);
if (found) return found;
}
}
return undefined;
}

export function getFirstChildHrefForPath(partialPath: string): string | undefined {
const prefix = partialPath.replace(/\/$/, '');
for (const tab of tabNavigation) {
for (const group of tab.groups) {
const found = findFirstHrefUnder(group.items, prefix);
if (found) return found;
}
}
return undefined;
}

// Find the active group within the Docs tab based on current path
export function getActiveGroup(currentPath: string): NavGroup | undefined {
const docsTab = tabNavigation[0]; // Docs tab
Expand Down
Loading