From 2d0c36bd95ee19dc0576eae651a66cf50de999db Mon Sep 17 00:00:00 2001 From: Duncan Crawbuck Date: Wed, 10 Jun 2026 15:23:44 -0700 Subject: [PATCH] Add hosted API docs links --- src/routes/$.tsx | 87 +++++++++++++++++++++++++++++--------------- src/routes/index.tsx | 35 +++++++++++++++--- 2 files changed, 88 insertions(+), 34 deletions(-) diff --git a/src/routes/$.tsx b/src/routes/$.tsx index b1f184c..2e5185e 100644 --- a/src/routes/$.tsx +++ b/src/routes/$.tsx @@ -1,5 +1,6 @@ import { createFileRoute, notFound } from "@tanstack/react-router"; import { DocsLayout } from "fumadocs-ui/layouts/notebook"; +import { getLayoutTabs, type LayoutTab } from "fumadocs-ui/layouts/shared"; import { createServerFn } from "@tanstack/react-start"; import { source } from "@/lib/source"; import browserCollections from "fumadocs-mdx:collections/browser"; @@ -23,6 +24,7 @@ import { } from "@/lib/metadata"; import { getSdkContextFromRouterPath, SDK_CONTEXT_SLUGS } from "@/lib/sdk-navigation"; import { buildDocsApiPath } from "@/lib/url-base"; +import { Code } from "lucide-react"; import { staticCacheMiddleware } from "@/lib/static-cache-middleware"; @@ -152,6 +154,7 @@ const clientLoader = browserCollections.docs.createClientLoader({ }); const NAVBAR_ONLY_TABS = new Set(["support", "changelog"]); const SDK_CONTEXT_SET = new Set(SDK_CONTEXT_SLUGS); +const API_REFERENCE_URL = "https://api.superwall.com/docs"; function parseSDKSubPath(url: string): string | null { const withoutBase = url.replace(/^\/docs/, "") || "/"; @@ -163,48 +166,74 @@ function parseSDKSubPath(url: string): string | null { return withoutBase.slice(sdkPrefix.length); } +function getDocsLayoutTabs( + pageTree: Awaited>, + currentSubPath: string | null, +): LayoutTab[] { + const tabs = getLayoutTabs(pageTree, { + transform: (option) => { + const tabPrefix = option.url.replace(/^\/docs\//, "").split("/")[0]; + if (NAVBAR_ONLY_TABS.has(tabPrefix)) return null; + + let { url } = option; + if (currentSubPath && option.urls) { + if (SDK_CONTEXT_SET.has(tabPrefix)) { + const candidate = `/docs/${tabPrefix}/${currentSubPath}`; + if (option.urls.has(candidate)) { + url = candidate; + } + } + } + + return { + ...option, + url, + icon: ( + + {option.icon} + + ), + props: { + ...option.props, + children: option.icon, + }, + }; + }, + }); + + tabs.push({ + title: "API", + url: API_REFERENCE_URL, + icon: ( + + + + ), + props: { + target: "_blank", + rel: "noreferrer noopener", + children: , + }, + }); + + return tabs; +} + function Page() { const loaderData = Route.useLoaderData(); const data = useFumadocsLoader(loaderData); const { nav, ...base } = baseOptions(); const currentSubPath = parseSDKSubPath(loaderData.url); + const layoutTabs = getDocsLayoutTabs(data.pageTree, currentSubPath); return ( { - const tabPrefix = option.url.replace(/^\/docs\//, "").split("/")[0]; - if (NAVBAR_ONLY_TABS.has(tabPrefix)) return null; - - let { url } = option; - if (currentSubPath && option.urls) { - if (SDK_CONTEXT_SET.has(tabPrefix)) { - const candidate = `/docs/${tabPrefix}/${currentSubPath}`; - if (option.urls.has(candidate)) { - url = candidate; - } - } - } - return { - ...option, - url, - icon: ( - - {option.icon} - - ), - props: { - ...option.props, - children: option.icon, - }, - }; - }, - }, prefetch: true, footer: , }} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 5d24b0b..a13a416 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -8,6 +8,7 @@ import { CircleHelp, History, AlertTriangle, + Code, Users, Cpu, } from "lucide-react"; @@ -123,6 +124,12 @@ const docsCards: DocCard[] = [ href: buildDocsPath("integrations"), icon: , }, + { + title: "API", + description: "Explore Superwall's hosted API reference.", + href: "https://api.superwall.com/docs", + icon: , + }, { title: "Support", description: "FAQs, troubleshooting guides, and help for common issues.", @@ -188,12 +195,10 @@ function OverviewCard({ title, description, href, icon }: DocCard) { }); const normalizedHref = normalizeDocsInternalHref(href); const resolvedHref = resolveSdkAwareDocsHref(normalizedHref, currentRouterPath); + const isExternal = /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(resolvedHref); - return ( - + const content = ( + <>
{icon} @@ -201,6 +206,26 @@ function OverviewCard({ title, description, href, icon }: DocCard) {

{title}

{description}

+ + ); + + const className = + "group flex flex-col gap-3 rounded-xl border border-fd-border bg-fd-secondary/50 p-5 transition-all duration-200 hover:border-fd-primary/40 hover:bg-fd-secondary"; + + if (isExternal) { + return ( + + {content} + + ); + } + + return ( + + {content} ); }