diff --git a/sidebars.js b/sidebars.js index ba42163641..2ade4fd228 100644 --- a/sidebars.js +++ b/sidebars.js @@ -2209,3 +2209,88 @@ module.exports = { 'tctl-v1/workflow', ], }; + +// --------------------------------------------------------------------------- +// Secondary navigation bar (proof of concept) +// +// Moves four sections out of the single `documentation` sidebar and gives each +// one its own sidebar, so a reader inside a section sees only that section's +// tree. src/components/SecondaryNav renders the tabs and works out which +// section the reader is in from the URL. +// +// Every other section stays in `documentation` and keeps today's sidebar. +// To undo the proof of concept, delete this block and the SecondaryNav import +// in src/theme/Navbar/index.js. +// --------------------------------------------------------------------------- + +const NAV_TAB_SECTIONS = [ + { sidebar: 'develop', category: 'Develop' }, + { sidebar: 'cloud', category: 'Temporal Cloud' }, + { sidebar: 'guides', category: 'Guides' }, + // The AI tab carries the Durable AI tree plus the standalone "Develop with + // AI" page, which lives at the top level of the documentation sidebar today. + { sidebar: 'ai', category: 'Durable AI', extraDocs: ['with-ai'] }, +]; + +// The tab label already names the section, so drop the wrapping category and +// promote its overview page to the first sidebar entry. +function flattenSection(category) { + const overview = category.link && category.link.type === 'doc' ? [category.link.id] : []; + return [...overview, ...category.items]; +} + +(function buildSectionSidebars(sidebars) { + const mainSidebar = sidebars.documentation; + const moved = new Set(); + + for (const section of NAV_TAB_SECTIONS) { + const category = mainSidebar.find((item) => item && item.type === 'category' && item.label === section.category); + + if (!category) { + throw new Error( + `Secondary nav: no "${section.category}" category found in the documentation sidebar. ` + + `Update NAV_TAB_SECTIONS in sidebars.js if the section was renamed or removed.` + ); + } + + const extraDocs = section.extraDocs || []; + moved.add(category); + extraDocs.forEach((id) => moved.add(id)); + + sidebars[section.sidebar] = [...flattenSection(category), ...extraDocs]; + } + + sidebars.documentation = mainSidebar.filter((item) => !moved.has(item)); +})(module.exports); + +// Develop splits one level further: each SDK gets its own sidebar, so a reader +// on /develop/go sees the Go tree instead of all eight. src/components/SdkNav +// renders the SDKs as a third row, shown only inside Develop. What is left in +// `develop` is the language-agnostic material. +(function buildSdkSidebars(sidebars) { + const developSidebar = sidebars.develop; + const moved = new Set(); + const sdkLinks = []; + + for (const { id } of SDKS) { + const category = developSidebar.find( + (item) => item && item.type === 'category' && item.link && item.link.id === `develop/${id}/index` + ); + + if (!category) { + throw new Error(`Secondary nav: no sidebar category found for the ${id} SDK (expected link develop/${id}/index).`); + } + + moved.add(category); + sidebars[`develop-${id}`] = flattenSection(category); + + // The SDKs stay listed in Develop as plain links. A doc can belong to only + // one sidebar, and its pages now live in the per-SDK sidebar above. + sdkLinks.push({ type: 'link', label: category.label, href: `/develop/${id}` }); + } + + const remaining = developSidebar.filter((item) => !moved.has(item)); + const afterOverview = remaining.indexOf('develop/index') + 1; + + sidebars.develop = [...remaining.slice(0, afterOverview), ...sdkLinks, ...remaining.slice(afterOverview)]; +})(module.exports); diff --git a/src/components/SdkNav/activeSdk.js b/src/components/SdkNav/activeSdk.js new file mode 100644 index 0000000000..9d17547dbc --- /dev/null +++ b/src/components/SdkNav/activeSdk.js @@ -0,0 +1,17 @@ +import { SDKS } from '@site/src/constants/sdks'; + +export const DEVELOP_SECTION = '/develop'; + +export function normalizePath(pathname) { + return pathname.replace(/\/+$/, '') || '/'; +} + +// The SDK row and the section tabs are mutually exclusive, so both rows read +// this to decide which one renders. +export function findActiveSdk(pathname) { + const path = normalizePath(pathname); + return SDKS.find(({ id }) => { + const base = `${DEVELOP_SECTION}/${id}`; + return path === base || path.startsWith(`${base}/`); + }); +} diff --git a/src/components/SdkNav/index.js b/src/components/SdkNav/index.js new file mode 100644 index 0000000000..aaac681556 --- /dev/null +++ b/src/components/SdkNav/index.js @@ -0,0 +1,45 @@ +import React from 'react'; +import Link from '@docusaurus/Link'; +import { useLocation } from '@docusaurus/router'; +import { SDKS } from '@site/src/constants/sdks'; +import { StylePickers, useNavStyle } from '@site/src/components/navStyle'; +import styles from '@site/src/components/SecondaryNav/styles.module.css'; +import { DEVELOP_SECTION, findActiveSdk } from './activeSdk'; +import sdkStyles from './styles.module.css'; + +export default function SdkNav() { + const activeSdk = findActiveSdk(useLocation().pathname); + const navStyle = useNavStyle(); + + if (!activeSdk) return null; + + const tabs = SDKS.map(({ id, label, icon: Icon }) => { + const active = id === activeSdk.id; + return ( + +