From a8ac411589fbf74ba263a2d4d13e7b77a0a34a09 Mon Sep 17 00:00:00 2001 From: Suhani Nagpal Date: Wed, 20 May 2026 12:22:08 +0530 Subject: [PATCH] fix(docs): make middle breadcrumb segments clickable (TH-3860) Breadcrumb segments for intermediate URL paths without their own page (e.g. /docs/quickstart, /docs/agent-playground/concepts) were rendered as non-clickable spans, which made the breadcrumb look broken next to the linked siblings. When no page exists at the intermediate URL but child pages exist in the nav tree, fall back to the first child href so the segment stays clickable. Linear: https://linear.app/future-agi/issue/TH-3860 --- src/layouts/DocsLayout.astro | 23 ++++++++++++++++------- src/lib/navigation.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/layouts/DocsLayout.astro b/src/layouts/DocsLayout.astro index 0b8f0682..eb7409ac 100644 --- a/src/layouts/DocsLayout.astro +++ b/src/layouts/DocsLayout.astro @@ -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: { @@ -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; @@ -98,8 +107,8 @@ if (breadcrumbs.length > 0) {
  • {i > 0 && /} {i < breadcrumbs.length - 1 ? ( - crumb.hasPage ? ( - {crumb.name} + crumb.hasLink && crumb.linkHref ? ( + {crumb.name} ) : ( {crumb.name} ) diff --git a/src/lib/navigation.ts b/src/lib/navigation.ts index f8c26b39..daacefd0 100644 --- a/src/lib/navigation.ts +++ b/src/lib/navigation.ts @@ -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