From f427b0994774847dddb12b724f9a204d9cbda4c5 Mon Sep 17 00:00:00 2001 From: khushalsonawat Date: Mon, 29 Jun 2026 16:34:30 +0530 Subject: [PATCH] feat(docs): reorder product dropdown by agent lifecycle Reorder the Docs section-switcher dropdown from alphabetical to agent-development-lifecycle order, grouped under lightweight phase headers (Start, Observe & diagnose, Evaluate & measure, Improve, Build & connect, Protect, Assistant, Reference). "Resources" is omitted and "Falcon AI" sits last. The change lands on the live dropdown in Sidebar.astro (Docs tab only; other tabs fall back to a single unlabelled list). A "More" bucket surfaces any future product that isn't placed, so nothing silently disappears. Remove the dead SectionSwitcher.astro and Breadcrumb.astro components: nothing rendered them (DocsLayout uses its own inline breadcrumb), and the build of all 931 pages passes without them. --- src/components/Breadcrumb.astro | 55 ------- src/components/SectionSwitcher.astro | 210 --------------------------- src/components/Sidebar.astro | 104 ++++++++++--- 3 files changed, 84 insertions(+), 285 deletions(-) delete mode 100644 src/components/Breadcrumb.astro delete mode 100644 src/components/SectionSwitcher.astro diff --git a/src/components/Breadcrumb.astro b/src/components/Breadcrumb.astro deleted file mode 100644 index 8b90bb7d..00000000 --- a/src/components/Breadcrumb.astro +++ /dev/null @@ -1,55 +0,0 @@ ---- -import SectionSwitcher from './SectionSwitcher.astro'; - -const currentPath = Astro.url.pathname; -const pathParts = currentPath.split('/').filter(Boolean); - -function toTitleCase(str: string) { - return str - .split('-') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); -} - -// Skip 'docs' from breadcrumb display since we show it via the section switcher -const breadcrumbParts = pathParts.filter(part => part !== 'docs'); -const breadcrumbs = breadcrumbParts.map((part, index) => { - // Build href from the original path parts - const originalIndex = pathParts.indexOf(part); - return { - label: toTitleCase(part), - href: '/' + pathParts.slice(0, originalIndex + 1).join('/'), - isLast: index === breadcrumbParts.length - 1, - }; -}); - -// Only show crumbs after the first one (which is shown in the section switcher) -const trailCrumbs = breadcrumbs.slice(1); ---- - - diff --git a/src/components/SectionSwitcher.astro b/src/components/SectionSwitcher.astro deleted file mode 100644 index 80bba85a..00000000 --- a/src/components/SectionSwitcher.astro +++ /dev/null @@ -1,210 +0,0 @@ ---- -/** - * Section Switcher Dropdown - * PostHog-style dropdown for switching between documentation sections. - * Shows the current section with an icon and lets users jump to any other section. - */ -import { tabNavigation, type NavTab } from '../lib/navigation'; - -const currentPath = Astro.url.pathname; - -// All navigable sections from the Docs tab -const docsTab = tabNavigation.find(t => t.tab === 'Docs'); -const sections = docsTab?.groups || []; - -// Find the current section based on URL -function getCurrentSection() { - // Check specific tabs first - for (const tab of tabNavigation) { - if (tab.href !== '/docs' && currentPath.startsWith(tab.href)) { - return { title: tab.tab, icon: tab.icon, href: tab.href }; - } - } - - // Check within docs groups - for (const group of sections) { - for (const item of group.items) { - if (item.href && (currentPath === item.href || currentPath.startsWith(item.href + '/'))) { - return { title: group.group, icon: group.icon || 'default', href: item.href }; - } - if (item.items) { - for (const child of item.items) { - if (child.href && (currentPath === child.href || currentPath.startsWith(child.href + '/'))) { - return { title: group.group, icon: group.icon || 'default', href: item.href || child.href }; - } - } - } - } - } - - return { title: 'Docs', icon: 'book', href: '/docs' }; -} - -const current = getCurrentSection(); - -// Build dropdown items from all sections + tabs -interface DropdownItem { - title: string; - icon: string; - href: string; - description?: string; -} - -const dropdownItems: DropdownItem[] = [ - ...sections.map(g => ({ - title: g.group, - icon: g.icon || 'default', - href: g.items[0]?.href || '/docs', - description: '', - })), - // Add other tabs - { title: 'Integrations', icon: 'plug', href: '/docs/integrations', description: '' }, - { title: 'Guides', icon: 'book', href: '/docs/cookbook', description: '' }, - { title: 'SDK Reference', icon: 'code', href: '/docs/sdk', description: '' }, - { title: 'API Reference', icon: 'webhook', href: '/docs/api', description: '' }, -]; - -const iconPaths: Record = { - 'rocket': 'M13 10V3L4 14h7v7l9-11h-7z', - 'table': 'M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z', - 'play': 'M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z M21 12a9 9 0 11-18 0 9 9 0 0118 0z', - 'chart': 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z', - 'zap': 'M13 10V3L4 14h7v7l9-11h-7z', - 'flask': 'M9 3v2m6-2v2M9 19h6m-3-11V3M5 21h14a2 2 0 002-2v-4a2 2 0 00-.586-1.414l-5-5A2 2 0 0014 8V3a1 1 0 00-1-1h-2a1 1 0 00-1 1v5a2 2 0 01-.586 1.414l-5 5A2 2 0 004 15v4a2 2 0 002 2z', - 'eye': 'M15 12a3 3 0 11-6 0 3 3 0 016 0z M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z', - 'compass': 'M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9', - 'gauge': 'M13 7h8m0 0v8m0-8l-8 8-4-4-6 6', - 'shield': 'M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z', - 'brain': 'M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z', - 'plug': 'M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1', - 'book': 'M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253', - 'code': 'M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4', - 'webhook': 'M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4', - 'default': 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z', -}; - -function getIconPath(icon: string): string { - return iconPaths[icon] || iconPaths.default; -} ---- - -
- - - - - -
- - diff --git a/src/components/Sidebar.astro b/src/components/Sidebar.astro index be417906..2df4e379 100644 --- a/src/components/Sidebar.astro +++ b/src/components/Sidebar.astro @@ -89,6 +89,62 @@ function getIconPath(icon?: string): string { return iconPaths[icon || 'default'] || iconPaths.default; } +// ── Lifecycle-ordered product dropdown (Docs tab only) ────────────────────── +// Reorders the Docs section dropdown from alphabetical to agent-development- +// lifecycle order, grouped under lightweight phase headers. This is dropdown-only +// and does NOT reorder the `groups` array in navigation.ts, so the page list below +// still follows nav order. "Resources" is intentionally omitted; "Falcon AI" sits +// last; a "Reference" group links out to the Integrations/Guides/SDK/API tabs. +// The dropdown only renders when a tab exposes multiple groups (today that's the +// Docs tab); any other multi-group tab falls back to a single unlabelled list. +interface DropdownItem { title: string; icon: string; href: string; } +interface DropdownSection { phase: string; items: DropdownItem[]; } + +const groupLookup = new Map( + allGroups.map(g => [g.group, { icon: g.icon || 'default', href: g.items[0]?.href || '/docs' }]) +); +// Single source for turning a group title into a dropdown item. +const toItem = (title: string): DropdownItem => { + const g = groupLookup.get(title); + return { title, icon: g?.icon || 'default', href: g?.href || '/docs' }; +}; + +const phasedOrder: { phase: string; titles: string[] }[] = [ + { phase: 'Start', titles: ['Get Started'] }, + { phase: 'Observe & diagnose', titles: ['Observability', 'Error Feed'] }, + { phase: 'Evaluate & measure', titles: ['Evaluation', 'Simulation', 'Dataset'] }, + { phase: 'Improve', titles: ['Optimization', 'Annotations'] }, + { phase: 'Build & connect', titles: ['Prompt', 'Prototype', 'Agent Playground', 'Knowledge Base', 'Agent Command Center'] }, + { phase: 'Protect', titles: ['Protect'] }, + { phase: 'Assistant', titles: ['Falcon AI'] }, +]; + +// Reference links live in their own top-level tabs; surface them at the bottom. +const referenceItems: DropdownItem[] = [ + { title: 'Integrations', icon: 'plug', href: '/docs/integrations' }, + { title: 'Guides', icon: 'book', href: '/docs/cookbook' }, + { title: 'SDK Reference', icon: 'code', href: '/docs/sdk' }, + { title: 'API Reference', icon: 'webhook', href: '/docs/api' }, +]; + +let dropdownSections: DropdownSection[]; +if (isDocsTab) { + dropdownSections = phasedOrder + .map(p => ({ phase: p.phase, items: p.titles.filter(t => groupLookup.has(t)).map(toItem) })) + .filter(s => s.items.length > 0); + + // Future-proofing: surface any Docs group not placed above (besides the hidden + // "Resources") under "More" so new products never silently vanish. + const placed = new Set([...phasedOrder.flatMap(p => p.titles), 'Resources']); + const leftovers = allGroups.map(g => g.group).filter(t => !placed.has(t)).map(toItem); + if (leftovers.length) dropdownSections.push({ phase: 'More', items: leftovers }); + + dropdownSections.push({ phase: 'Reference', items: referenceItems }); +} else { + // Other multi-group tabs: one unlabelled section (empty phase hides the header). + dropdownSections = [{ phase: '', items: allGroups.map(g => toItem(g.group)) }]; +} + // Infer HTTP method from API endpoint title for sidebar badges const isApiTab = activeTab?.tab === 'API'; @@ -139,26 +195,34 @@ function inferApiMethod(title: string): { method: string; css: string } | null { class="absolute left-3 right-3 mt-1 py-1 bg-[var(--color-bg-secondary)] border border-[var(--color-border-default)] rounded-xl shadow-xl shadow-black/20 z-50 hidden opacity-0 transition-all duration-150 max-h-[60vh] overflow-y-auto hide-scrollbar" data-section-dropdown > - {allGroups.map((group) => { - const isCurrent = group.group === activeGroup?.group; - const firstHref = group.items[0]?.href || '/docs'; - return ( - - - - - {group.group} - - ); - })} + {dropdownSections.map((section, sectionIndex) => ( +
0 && "mt-1.5 pt-1.5 border-t border-[var(--color-border-default)]/60"]}> + {section.phase && ( +
+ {section.phase} +
+ )} + {section.items.map((item) => { + const isCurrent = item.title === activeGroup?.group; + return ( + + + + + {item.title} + + ); + })} +
+ ))} )}