From 3bd08a46fe940dbdcfbe8eca75f92a41d9245e7f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 3 Feb 2026 04:45:59 +0000 Subject: [PATCH 1/5] Add next/previous section navigation to API, MAPI, and CLI reference pages - Added getSectionNavigation helper to ApiReferenceLayout to compute prev/next sections - Added Page.ContentFooter with section navigation to ApiReferenceLayout - Added getSectionNavigation helper to CliReferenceLayout - Added Page.ContentFooter with section navigation to CliReferenceLayout - Added missing currentPath prop to api-reference and mapi-reference index pages This allows users to navigate between sections (e.g., from /api-reference/overview to /api-reference/workflows) using next/previous buttons at the bottom of the page. Co-authored-by: chris --- .../api-reference/ApiReferenceLayout.tsx | 65 ++++++++++++++++- layouts/CliReferenceLayout.tsx | 69 ++++++++++++++++++- pages/api-reference/index.tsx | 1 + pages/mapi-reference/index.tsx | 1 + 4 files changed, 134 insertions(+), 2 deletions(-) diff --git a/components/api-reference/ApiReferenceLayout.tsx b/components/api-reference/ApiReferenceLayout.tsx index 770cff5ce..db5829f36 100644 --- a/components/api-reference/ApiReferenceLayout.tsx +++ b/components/api-reference/ApiReferenceLayout.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useMemo, useRef } from "react"; import { useRouter } from "next/router"; import Meta from "@/components/Meta"; import { Page as TelegraphPage } from "@/components/ui/Page"; @@ -12,6 +12,12 @@ interface Breadcrumb { href: string; } +type PageNeighbor = { + title: string; + path: string; + slug: string; +}; + interface ApiReferenceLayoutProps { children: React.ReactNode; sidebarData: SidebarData; @@ -52,6 +58,53 @@ function convertToLegacySidebarFormat( return [...preSidebarContent, ...resourceSections]; } +/** + * Get the previous and next sections based on the current path. + * Sections are the top-level items in the sidebar (overview + resources). + */ +function getSectionNavigation( + currentPath: string | undefined, + sidebarContent: LegacySidebarSection[], +): { prevSection: PageNeighbor | undefined; nextSection: PageNeighbor | undefined } { + if (!currentPath) { + return { prevSection: undefined, nextSection: undefined }; + } + + // Find which section the current path belongs to + const currentSectionIndex = sidebarContent.findIndex((section) => { + return currentPath.startsWith(section.slug); + }); + + if (currentSectionIndex === -1) { + return { prevSection: undefined, nextSection: undefined }; + } + + let prevSection: PageNeighbor | undefined = undefined; + let nextSection: PageNeighbor | undefined = undefined; + + // Get previous section + if (currentSectionIndex > 0) { + const prev = sidebarContent[currentSectionIndex - 1]; + prevSection = { + title: prev.title || "", + path: prev.slug, + slug: prev.slug, + }; + } + + // Get next section + if (currentSectionIndex < sidebarContent.length - 1) { + const next = sidebarContent[currentSectionIndex + 1]; + nextSection = { + title: next.title || "", + path: next.slug, + slug: next.slug, + }; + } + + return { prevSection, nextSection }; +} + export function ApiReferenceLayout({ children, sidebarData, @@ -81,6 +134,12 @@ export function ApiReferenceLayout({ preSidebarContent, ); + // Get section navigation (prev/next sections) + const { prevSection, nextSection } = useMemo( + () => getSectionNavigation(currentPath, sidebarContent), + [currentPath, sidebarContent], + ); + // For per-resource pages, currentPath is the resource base path (e.g., /api-reference/users) // This enables same-page routing for links within the current resource const sidebarContextValue = { @@ -131,6 +190,10 @@ export function ApiReferenceLayout({ } /> {children} + diff --git a/layouts/CliReferenceLayout.tsx b/layouts/CliReferenceLayout.tsx index ed95133e0..faa1e94cf 100644 --- a/layouts/CliReferenceLayout.tsx +++ b/layouts/CliReferenceLayout.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useMemo } from "react"; import { Page } from "@/components/ui/Page"; import { slugToPaths } from "../lib/content"; import Meta from "../components/Meta"; @@ -7,6 +7,60 @@ import { useInitialScrollState } from "../components/ui/Page/helpers"; import { CLI_SIDEBAR } from "../data/sidebars/cliSidebar"; import { ContentActions } from "../components/ui/ContentActions"; import { useScrollToTop } from "../hooks/useScrollToTop"; +import { SidebarContent } from "../data/types"; + +type PageNeighbor = { + title: string; + path: string; + slug: string; +}; + +/** + * Get the previous and next sections based on the current path. + * Sections are the top-level items in the CLI sidebar. + */ +function getSectionNavigation( + currentPath: string | undefined, + sidebarContent: SidebarContent[], +): { prevSection: PageNeighbor | undefined; nextSection: PageNeighbor | undefined } { + if (!currentPath) { + return { prevSection: undefined, nextSection: undefined }; + } + + // Find which section the current path belongs to + const currentSectionIndex = sidebarContent.findIndex((section) => { + return currentPath.startsWith(section.slug); + }); + + if (currentSectionIndex === -1) { + return { prevSection: undefined, nextSection: undefined }; + } + + let prevSection: PageNeighbor | undefined = undefined; + let nextSection: PageNeighbor | undefined = undefined; + + // Get previous section + if (currentSectionIndex > 0) { + const prev = sidebarContent[currentSectionIndex - 1]; + prevSection = { + title: prev.title, + path: prev.slug, + slug: prev.slug, + }; + } + + // Get next section + if (currentSectionIndex < sidebarContent.length - 1) { + const next = sidebarContent[currentSectionIndex + 1]; + nextSection = { + title: next.title, + path: next.slug, + slug: next.slug, + }; + } + + return { prevSection, nextSection }; +} interface CliReferenceLayoutProps { frontMatter: { @@ -37,6 +91,15 @@ export const CliReferenceLayout = ({ const resource = router.query.resource as string; const mdPath = resource ? `/cli/${resource}.md` : undefined; + // Build the current section path for navigation + const currentSectionPath = resource ? `/cli/${resource}` : "/cli/overview"; + + // Get section navigation (prev/next sections) + const { prevSection, nextSection } = useMemo( + () => getSectionNavigation(currentSectionPath, CLI_SIDEBAR), + [currentSectionPath], + ); + return ( {children} + diff --git a/pages/api-reference/index.tsx b/pages/api-reference/index.tsx index 2a0961597..d91f3f973 100644 --- a/pages/api-reference/index.tsx +++ b/pages/api-reference/index.tsx @@ -27,6 +27,7 @@ function ApiReferenceOverview({ preSidebarContent={API_REFERENCE_OVERVIEW_CONTENT} title="API reference" description="Complete reference documentation for the Knock API." + currentPath="/api-reference/overview" > diff --git a/pages/mapi-reference/index.tsx b/pages/mapi-reference/index.tsx index ae7925df4..5b3ce4866 100644 --- a/pages/mapi-reference/index.tsx +++ b/pages/mapi-reference/index.tsx @@ -27,6 +27,7 @@ function MapiReferenceOverview({ preSidebarContent={MAPI_REFERENCE_OVERVIEW_CONTENT} title="Management API reference" description="Complete reference documentation for the Knock Management API." + currentPath="/mapi-reference/overview" > From 15e979d5b6806e193a042de1d6dc8d7661e71038 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 3 Feb 2026 04:50:37 +0000 Subject: [PATCH 2/5] Fix formatting issues from Prettier Co-authored-by: chris --- components/api-reference/ApiReferenceLayout.tsx | 5 ++++- layouts/CliReferenceLayout.tsx | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/components/api-reference/ApiReferenceLayout.tsx b/components/api-reference/ApiReferenceLayout.tsx index db5829f36..2b5740cd5 100644 --- a/components/api-reference/ApiReferenceLayout.tsx +++ b/components/api-reference/ApiReferenceLayout.tsx @@ -65,7 +65,10 @@ function convertToLegacySidebarFormat( function getSectionNavigation( currentPath: string | undefined, sidebarContent: LegacySidebarSection[], -): { prevSection: PageNeighbor | undefined; nextSection: PageNeighbor | undefined } { +): { + prevSection: PageNeighbor | undefined; + nextSection: PageNeighbor | undefined; +} { if (!currentPath) { return { prevSection: undefined, nextSection: undefined }; } diff --git a/layouts/CliReferenceLayout.tsx b/layouts/CliReferenceLayout.tsx index faa1e94cf..ae9aee21e 100644 --- a/layouts/CliReferenceLayout.tsx +++ b/layouts/CliReferenceLayout.tsx @@ -22,7 +22,10 @@ type PageNeighbor = { function getSectionNavigation( currentPath: string | undefined, sidebarContent: SidebarContent[], -): { prevSection: PageNeighbor | undefined; nextSection: PageNeighbor | undefined } { +): { + prevSection: PageNeighbor | undefined; + nextSection: PageNeighbor | undefined; +} { if (!currentPath) { return { prevSection: undefined, nextSection: undefined }; } From 5e03be2e6a9f543f8d20852397a26fac83f81548 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 3 Feb 2026 15:00:55 +0000 Subject: [PATCH 3/5] Add scroll-to-top behavior when navigating between sections - Added useEffect to scroll to top when currentPath changes in ApiReferenceLayout - Added useEffect to scroll to top when resource changes in CliReferenceLayout - Removed unused useScrollToTop hook from CliReferenceLayout (was looking for non-existent .main-content class) Co-authored-by: chris --- .../api-reference/ApiReferenceLayout.tsx | 5 +++++ layouts/CliReferenceLayout.tsx | 18 +++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/components/api-reference/ApiReferenceLayout.tsx b/components/api-reference/ApiReferenceLayout.tsx index 2b5740cd5..60fc4246f 100644 --- a/components/api-reference/ApiReferenceLayout.tsx +++ b/components/api-reference/ApiReferenceLayout.tsx @@ -129,6 +129,11 @@ export function ApiReferenceLayout({ }; }, [router]); + // Scroll to top when navigating to a new section + useEffect(() => { + window.scrollTo(0, 0); + }, [currentPath]); + const basePath = router.pathname.split("/")[1]; const canonicalPath = currentPath || `/${basePath}`; diff --git a/layouts/CliReferenceLayout.tsx b/layouts/CliReferenceLayout.tsx index ae9aee21e..04436b134 100644 --- a/layouts/CliReferenceLayout.tsx +++ b/layouts/CliReferenceLayout.tsx @@ -1,12 +1,10 @@ -import React, { useMemo } from "react"; +import React, { useEffect, useMemo } from "react"; import { Page } from "@/components/ui/Page"; -import { slugToPaths } from "../lib/content"; import Meta from "../components/Meta"; import { useRouter } from "next/router"; import { useInitialScrollState } from "../components/ui/Page/helpers"; import { CLI_SIDEBAR } from "../data/sidebars/cliSidebar"; import { ContentActions } from "../components/ui/ContentActions"; -import { useScrollToTop } from "../hooks/useScrollToTop"; import { SidebarContent } from "../data/types"; type PageNeighbor = { @@ -82,16 +80,18 @@ export const CliReferenceLayout = ({ }: CliReferenceLayoutProps) => { const router = useRouter(); useInitialScrollState(); - let paths = slugToPaths(router.query.slug); - - useScrollToTop(paths); - - // Build canonical path from the current route - const canonicalPath = router.asPath.split("#")[0].split("?")[0]; // Get the resource name from the route (e.g., "overview", "resources", "workflow") // This is the first segment after /cli/ and stays constant regardless of scroll position const resource = router.query.resource as string; + + // Scroll to top when navigating to a new section (resource changes) + useEffect(() => { + window.scrollTo(0, 0); + }, [resource]); + + // Build canonical path from the current route + const canonicalPath = router.asPath.split("#")[0].split("?")[0]; const mdPath = resource ? `/cli/${resource}.md` : undefined; // Build the current section path for navigation From f089afc487543eed0b1083c7077ff99b6df51d35 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 3 Feb 2026 15:10:40 +0000 Subject: [PATCH 4/5] Revert "Add scroll-to-top behavior when navigating between sections" This reverts commit 5e03be2e6a9f543f8d20852397a26fac83f81548. --- .../api-reference/ApiReferenceLayout.tsx | 5 ----- layouts/CliReferenceLayout.tsx | 18 +++++++++--------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/components/api-reference/ApiReferenceLayout.tsx b/components/api-reference/ApiReferenceLayout.tsx index 60fc4246f..2b5740cd5 100644 --- a/components/api-reference/ApiReferenceLayout.tsx +++ b/components/api-reference/ApiReferenceLayout.tsx @@ -129,11 +129,6 @@ export function ApiReferenceLayout({ }; }, [router]); - // Scroll to top when navigating to a new section - useEffect(() => { - window.scrollTo(0, 0); - }, [currentPath]); - const basePath = router.pathname.split("/")[1]; const canonicalPath = currentPath || `/${basePath}`; diff --git a/layouts/CliReferenceLayout.tsx b/layouts/CliReferenceLayout.tsx index 04436b134..ae9aee21e 100644 --- a/layouts/CliReferenceLayout.tsx +++ b/layouts/CliReferenceLayout.tsx @@ -1,10 +1,12 @@ -import React, { useEffect, useMemo } from "react"; +import React, { useMemo } from "react"; import { Page } from "@/components/ui/Page"; +import { slugToPaths } from "../lib/content"; import Meta from "../components/Meta"; import { useRouter } from "next/router"; import { useInitialScrollState } from "../components/ui/Page/helpers"; import { CLI_SIDEBAR } from "../data/sidebars/cliSidebar"; import { ContentActions } from "../components/ui/ContentActions"; +import { useScrollToTop } from "../hooks/useScrollToTop"; import { SidebarContent } from "../data/types"; type PageNeighbor = { @@ -80,18 +82,16 @@ export const CliReferenceLayout = ({ }: CliReferenceLayoutProps) => { const router = useRouter(); useInitialScrollState(); + let paths = slugToPaths(router.query.slug); - // Get the resource name from the route (e.g., "overview", "resources", "workflow") - // This is the first segment after /cli/ and stays constant regardless of scroll position - const resource = router.query.resource as string; - - // Scroll to top when navigating to a new section (resource changes) - useEffect(() => { - window.scrollTo(0, 0); - }, [resource]); + useScrollToTop(paths); // Build canonical path from the current route const canonicalPath = router.asPath.split("#")[0].split("?")[0]; + + // Get the resource name from the route (e.g., "overview", "resources", "workflow") + // This is the first segment after /cli/ and stays constant regardless of scroll position + const resource = router.query.resource as string; const mdPath = resource ? `/cli/${resource}.md` : undefined; // Build the current section path for navigation From 4aa7cce7355de4da4cc018863904090567e47cfb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 24 Feb 2026 04:04:00 +0000 Subject: [PATCH 5/5] Trigger Vercel deployment retry Co-authored-by: Chris Bell