Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 58 additions & 29 deletions src/routes/$.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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";

Expand Down Expand Up @@ -152,6 +154,7 @@ const clientLoader = browserCollections.docs.createClientLoader({
});
const NAVBAR_ONLY_TABS = new Set(["support", "changelog"]);
const SDK_CONTEXT_SET = new Set<string>(SDK_CONTEXT_SLUGS);
const API_REFERENCE_URL = "https://api.superwall.com/docs";

function parseSDKSubPath(url: string): string | null {
const withoutBase = url.replace(/^\/docs/, "") || "/";
Expand All @@ -163,48 +166,74 @@ function parseSDKSubPath(url: string): string | null {
return withoutBase.slice(sdkPrefix.length);
}

function getDocsLayoutTabs(
pageTree: Awaited<ReturnType<typeof source.serializePageTree>>,
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: (
<span className="flex size-full items-center justify-center [&_svg]:!size-5">
{option.icon}
</span>
),
props: {
...option.props,
children: option.icon,
},
};
},
});

tabs.push({
title: "API",
url: API_REFERENCE_URL,
icon: (
<span className="flex size-full items-center justify-center [&_svg]:!size-5">
<Code />
</span>
),
props: {
target: "_blank",
rel: "noreferrer noopener",
children: <Code />,
},
});

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 (
<DocsLayout
{...base}
tabMode="navbar"
tabs={layoutTabs}
nav={{ ...nav, mode: "auto" }}
tree={data.pageTree}
sidebar={{
tabs: {
transform: (option, node) => {
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: (
<span className="flex size-full items-center justify-center [&_svg]:!size-5">
{option.icon}
</span>
),
props: {
...option.props,
children: option.icon,
},
};
},
},
prefetch: true,
footer: <SidebarDiscordLink />,
}}
Expand Down
35 changes: 30 additions & 5 deletions src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
CircleHelp,
History,
AlertTriangle,
Code,
Users,
Cpu,
} from "lucide-react";
Expand Down Expand Up @@ -123,6 +124,12 @@ const docsCards: DocCard[] = [
href: buildDocsPath("integrations"),
icon: <Puzzle className="size-5" />,
},
{
title: "API",
description: "Explore Superwall's hosted API reference.",
href: "https://api.superwall.com/docs",
icon: <Code className="size-5" />,
},
{
title: "Support",
description: "FAQs, troubleshooting guides, and help for common issues.",
Expand Down Expand Up @@ -188,19 +195,37 @@ 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 (
<Link
to={toRouterPath(resolvedHref)}
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"
>
const content = (
<>
<div className="flex items-center gap-3">
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-fd-accent text-fd-foreground transition-colors duration-200 group-hover:bg-fd-primary/10 group-hover:text-fd-primary">
{icon}
</div>
<h3 className="font-semibold text-fd-foreground">{title}</h3>
</div>
<p className="text-sm leading-relaxed text-fd-muted-foreground">{description}</p>
</>
);

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 (
<a href={resolvedHref} target="_blank" rel="noreferrer noopener" className={className}>
{content}
</a>
);
}

return (
<Link
to={toRouterPath(resolvedHref)}
className={className}
>
{content}
</Link>
);
}
Expand Down
Loading