diff --git a/content/docs/kmp/guides/vibe-coding.mdx b/content/docs/kmp/guides/vibe-coding.mdx new file mode 100644 index 00000000..b34e4c31 --- /dev/null +++ b/content/docs/kmp/guides/vibe-coding.mdx @@ -0,0 +1,6 @@ +--- +title: "Vibe Coding" +description: "How to Vibe Code using the knowledge of the Superwall Docs" +--- + +../../../shared/vibe-coding.mdx \ No newline at end of file diff --git a/content/docs/kmp/meta.json b/content/docs/kmp/meta.json index afa95270..07ea704b 100644 --- a/content/docs/kmp/meta.json +++ b/content/docs/kmp/meta.json @@ -1,5 +1,6 @@ { "title": "KMP SDK", + "description": "Beta", "icon": "Layers", "root": true, "pages": [ @@ -20,6 +21,7 @@ "guides/using-superwall-delegate", "guides/3rd-party-analytics", "guides/handling-deep-links", + "guides/vibe-coding", "[Example App](https://github.com/superwall/Superwall-KMP/tree/main/sample)" ] } diff --git a/content/docs/unity/guides/vibe-coding.mdx b/content/docs/unity/guides/vibe-coding.mdx new file mode 100644 index 00000000..b34e4c31 --- /dev/null +++ b/content/docs/unity/guides/vibe-coding.mdx @@ -0,0 +1,6 @@ +--- +title: "Vibe Coding" +description: "How to Vibe Code using the knowledge of the Superwall Docs" +--- + +../../../shared/vibe-coding.mdx \ No newline at end of file diff --git a/content/docs/unity/meta.json b/content/docs/unity/meta.json index 5b50efb9..b15a8a43 100644 --- a/content/docs/unity/meta.json +++ b/content/docs/unity/meta.json @@ -19,6 +19,7 @@ "guides/using-superwall-delegate", "guides/custom-purchase-controller", "guides/advanced-configuration", + "guides/vibe-coding", "---SDK Reference---", "sdk-reference/index", diff --git a/content/docs/web/guides/vibe-coding.mdx b/content/docs/web/guides/vibe-coding.mdx new file mode 100644 index 00000000..b34e4c31 --- /dev/null +++ b/content/docs/web/guides/vibe-coding.mdx @@ -0,0 +1,6 @@ +--- +title: "Vibe Coding" +description: "How to Vibe Code using the knowledge of the Superwall Docs" +--- + +../../../shared/vibe-coding.mdx \ No newline at end of file diff --git a/content/docs/web/meta.json b/content/docs/web/meta.json index 64da908d..5570d2d2 100644 --- a/content/docs/web/meta.json +++ b/content/docs/web/meta.json @@ -1,5 +1,6 @@ { "title": "Web SDK", + "description": "Beta", "icon": "Globe", "root": true, "pages": [ @@ -18,6 +19,7 @@ "guides/server-side-gating", "guides/purchases", "guides/events", + "guides/vibe-coding", "[Example Apps](https://github.com/superwall/Superwall-Web/tree/main/example)" ] } diff --git a/content/shared/vibe-coding.mdx b/content/shared/vibe-coding.mdx index 61faebd7..1781a837 100644 --- a/content/shared/vibe-coding.mdx +++ b/content/shared/vibe-coding.mdx @@ -87,10 +87,14 @@ The Superwall Docs website has `llms.txt` and `llms-full.txt` files, in total an | ------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | All | [`llms.txt`](https://superwall.com/docs/llms.txt) | [`llms-full.txt`](https://superwall.com/docs/llms-full.txt) | | Dashboard | [`dashboard/llms.txt`](https://superwall.com/docs/dashboard/llms.txt) | [`dashboard/llms-full.txt`](https://superwall.com/docs/dashboard/llms-full.txt) | +| Agents | [`agents/llms.txt`](https://superwall.com/docs/agents/llms.txt) | [`agents/llms-full.txt`](https://superwall.com/docs/agents/llms-full.txt) | | iOS | [`ios/llms.txt`](https://superwall.com/docs/ios/llms.txt) | [`ios/llms-full.txt`](https://superwall.com/docs/ios/llms-full.txt) | | Android | [`android/llms.txt`](https://superwall.com/docs/android/llms.txt) | [`android/llms-full.txt`](https://superwall.com/docs/android/llms-full.txt) | | Flutter | [`flutter/llms.txt`](https://superwall.com/docs/flutter/llms.txt) | [`flutter/llms-full.txt`](https://superwall.com/docs/flutter/llms-full.txt) | | Expo | [`expo/llms.txt`](https://superwall.com/docs/expo/llms.txt) | [`expo/llms-full.txt`](https://superwall.com/docs/expo/llms-full.txt) | +| KMP (Beta) | [`kmp/llms.txt`](https://superwall.com/docs/kmp/llms.txt) | [`kmp/llms-full.txt`](https://superwall.com/docs/kmp/llms-full.txt) | +| Unity (Beta) | [`unity/llms.txt`](https://superwall.com/docs/unity/llms.txt) | [`unity/llms-full.txt`](https://superwall.com/docs/unity/llms-full.txt) | +| Web (Beta) | [`web/llms.txt`](https://superwall.com/docs/web/llms.txt) | [`web/llms-full.txt`](https://superwall.com/docs/web/llms-full.txt) | | React Native (Deprecated) | [`react-native/llms.txt`](https://superwall.com/docs/react-native/llms.txt) | [`react-native/llms-full.txt`](https://superwall.com/docs/react-native/llms-full.txt) | | Integrations | [`integrations/llms.txt`](https://superwall.com/docs/integrations/llms.txt) | [`integrations/llms-full.txt`](https://superwall.com/docs/integrations/llms-full.txt) | | Web Checkout | [`web-checkout/llms.txt`](https://superwall.com/docs/web-checkout/llms.txt) | [`web-checkout/llms-full.txt`](https://superwall.com/docs/web-checkout/llms-full.txt) | diff --git a/src/components/DocsHeader.tsx b/src/components/DocsHeader.tsx index 0ecac2fd..6d0fa9fe 100644 --- a/src/components/DocsHeader.tsx +++ b/src/components/DocsHeader.tsx @@ -1,6 +1,7 @@ "use client"; import { cn } from "@/lib/cn"; +import { getSdkTabLabel, isSdkTab } from "@/lib/sdk-tabs"; import { buttonVariants } from "fumadocs-ui/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger } from "fumadocs-ui/components/ui/popover"; import { @@ -15,31 +16,6 @@ import Link from "fumadocs-core/link"; import { ChevronDown, Languages, PanelLeft } from "lucide-react"; import { Fragment, useMemo, useRef, useState, type ComponentProps, type PointerEvent } from "react"; -const SDK_TAB_LABELS: Record = { - ios: "iOS", - android: "Android", - expo: "Expo", - flutter: "Flutter", - unity: "Unity", - "react-native": "React Native", - community: "Community", -}; - -const SDK_TAB_SLUGS = new Set(Object.keys(SDK_TAB_LABELS)); - -function getTabPrefix(url: string) { - return url.replace(/^\/docs\//, "").split("/")[0] ?? ""; -} - -function isSdkTab(tab: LayoutTab) { - return SDK_TAB_SLUGS.has(getTabPrefix(tab.url)); -} - -function getSdkTabLabel(tab: LayoutTab) { - const prefix = getTabPrefix(tab.url); - return SDK_TAB_LABELS[prefix] ?? String(tab.title); -} - function getSdkTabDescription(tab: LayoutTab) { return tab.description; } diff --git a/src/components/DocsSidebar.tsx b/src/components/DocsSidebar.tsx new file mode 100644 index 00000000..65ba9e64 --- /dev/null +++ b/src/components/DocsSidebar.tsx @@ -0,0 +1,528 @@ +"use client"; + +/** + * Adapted from `fumadocs-ui/layouts/notebook/slots/sidebar`, so the tabs dropdown can + * group SDK tabs into their own section at the end of the list. + */ + +import { cn } from "@/lib/cn"; +import { isSdkTab } from "@/lib/sdk-tabs"; +import { buttonVariants } from "fumadocs-ui/components/ui/button"; +import { Popover, PopoverContent, PopoverTrigger } from "fumadocs-ui/components/ui/popover"; +import { + SidebarContent as BaseSidebarContent, + SidebarFolder as BaseSidebarFolder, + SidebarFolderContent as BaseSidebarFolderContent, + SidebarFolderLink as BaseSidebarFolderLink, + SidebarFolderTrigger as BaseSidebarFolderTrigger, + SidebarItem as BaseSidebarItem, + SidebarSeparator as BaseSidebarSeparator, + SidebarDrawerContent, + SidebarDrawerOverlay, + SidebarViewport, + useFolder, + useFolderDepth, + useSidebar, +} from "fumadocs-ui/components/sidebar/base"; +import { createLinkItemRenderer } from "fumadocs-ui/components/sidebar/link-item"; +import { createPageTreeRenderer } from "fumadocs-ui/components/sidebar/page-tree"; +import { useNotebookLayout } from "fumadocs-ui/layouts/notebook"; +import { SidebarCollapseTrigger, SidebarTrigger } from "fumadocs-ui/layouts/notebook/slots/sidebar"; +import type { SidebarProps } from "fumadocs-ui/layouts/notebook/slots/sidebar"; +import { isLayoutTabActive, LinkItem, type LayoutTab } from "fumadocs-ui/layouts/shared"; +import { usePathname } from "fumadocs-core/framework"; +import Link from "fumadocs-core/link"; +import { cva } from "class-variance-authority"; +import { Check, ChevronsUpDown, Languages, SidebarIcon, X } from "lucide-react"; +import { + createElement, + Fragment, + useMemo, + useRef, + useState, + type ComponentProps, + type Ref, +} from "react"; + +const itemVariants = cva( + "relative flex flex-row items-center gap-2 rounded-lg p-2 text-start text-fd-muted-foreground wrap-anywhere [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + link: "transition-colors hover:bg-fd-accent/50 hover:text-fd-accent-foreground/80 hover:transition-none data-[active=true]:bg-fd-primary/10 data-[active=true]:text-fd-primary data-[active=true]:hover:transition-colors", + button: + "transition-colors hover:bg-fd-accent/50 hover:text-fd-accent-foreground/80 hover:transition-none", + }, + highlight: { + true: "data-[active=true]:before:content-[''] data-[active=true]:before:bg-fd-primary data-[active=true]:before:absolute data-[active=true]:before:w-px data-[active=true]:before:inset-y-2.5 data-[active=true]:before:start-2.5", + }, + }, + }, +); + +function getItemOffset(depth: number) { + return `calc(${2 + 3 * depth} * var(--spacing))`; +} + +function mergeRefs(...refs: (Ref | undefined)[]) { + return (value: T | null) => { + for (const ref of refs) { + if (typeof ref === "function") ref(value); + else if (ref) (ref as { current: T | null }).current = value; + } + }; +} + +function SidebarContent({ ref: refProp, className, children, ...props }: ComponentProps<"aside">) { + const { + props: { nav }, + } = useNotebookLayout(); + const navMode = nav?.mode ?? "auto"; + const ref = useRef(null); + + return ( + + {({ collapsed, hovered, ref: asideRef, ...rest }) => ( +
+ {collapsed &&
} + +
+ )} + + ); +} + +function SidebarDrawer({ children, className, ...props }: ComponentProps<"aside">) { + return ( + <> + + + {children} + + + ); +} + +function SidebarFolder(props: ComponentProps) { + return ; +} + +function SidebarSeparator({ + className, + style, + children, + ...props +}: ComponentProps) { + const depth = useFolderDepth(); + + return ( + + {children} + + ); +} + +function SidebarItem({ + className, + style, + children, + ...props +}: ComponentProps) { + const depth = useFolderDepth(); + + return ( + = 1 }), className)} + style={{ paddingInlineStart: getItemOffset(depth), ...style }} + {...props} + > + {children} + + ); +} + +function SidebarFolderTrigger({ + className, + style, + ...props +}: ComponentProps) { + const { depth, collapsible } = useFolder() ?? { depth: 1, collapsible: true }; + + return ( + + {props.children} + + ); +} + +function SidebarFolderLink({ + className, + style, + ...props +}: ComponentProps) { + const depth = useFolderDepth(); + + return ( + 1 }), "w-full", className)} + style={{ paddingInlineStart: getItemOffset(depth - 1), ...style }} + {...props} + > + {props.children} + + ); +} + +function SidebarFolderContent({ + className, + children, + ...props +}: ComponentProps) { + return ( + +
{children}
+
+ ); +} + +const SidebarPageTree = createPageTreeRenderer({ + SidebarFolder, + SidebarFolderContent, + SidebarFolderLink, + SidebarFolderTrigger, + SidebarItem, + SidebarSeparator, +}); + +const SidebarLinkItem = createLinkItemRenderer({ + SidebarFolder, + SidebarFolderContent, + SidebarFolderLink, + SidebarFolderTrigger, + SidebarItem, +}); + +export function DocsSidebar({ + banner, + footer, + components, + collapsible = true, + ...rest +}: SidebarProps) { + const { + menuItems, + slots, + props: { nav, tabs, tabMode }, + } = useNotebookLayout(); + const navMode = nav?.mode ?? "auto"; + const iconLinks = menuItems.filter((item) => item.type === "icon"); + + function renderHeader(props: ComponentProps<"div">) { + if (typeof banner === "function") return createElement(banner, props); + + return ( +
+ {props.children} + {banner} +
+ ); + } + + function renderFooter(props: ComponentProps<"div">) { + if (typeof footer === "function") return createElement(footer, props); + + return ( +
+ {props.children} + {footer} +
+ ); + } + + const viewport = ( + + {menuItems + .filter((item) => item.type !== "icon") + .map((item, i, arr) => ( + + ))} + + + ); + + return ( + <> + + {renderHeader({ + children: ( + <> + {navMode === "auto" && ( +
+ {slots.navTitle && ( + + )} + {nav?.children} + {collapsible && ( + + + + )} +
+ )} + {tabs.length > 0 && ( + + )} + + ), + })} + {viewport} + {renderFooter({ + className: cn( + "hidden flex-row text-fd-muted-foreground items-center border-t px-4 py-2.5", + iconLinks.length > 0 && "max-lg:flex", + ), + children: iconLinks.map((item, i) => ( + + {item.icon} + + )), + })} +
+ + {renderHeader({ + children: ( + <> + + + + {tabs.length > 0 && } + + ), + })} + {viewport} + {renderFooter({ + className: cn( + "hidden flex-row text-fd-muted-foreground items-center border-t p-4 pt-2 justify-end", + (slots.languageSelect || slots.themeSwitch) && "flex", + iconLinks.length > 0 && "max-lg:flex", + ), + children: ( + <> + {iconLinks.map((item, i) => ( + + {item.icon} + + ))} + {slots.languageSelect && ( + + + + )} + {slots.themeSwitch && } + + ), + })} + + + ); +} + +type TabSection = { title: string; options: LayoutTab[] }; + +function groupTabs(options: LayoutTab[]): TabSection[] { + const sections: TabSection[] = [ + { title: "Docs", options: options.filter((option) => !isSdkTab(option)) }, + { title: "SDKs", options: options.filter(isSdkTab) }, + ]; + + return sections.filter((section) => section.options.length > 0); +} + +function SidebarTabsDropdown({ + options, + placeholder, + ...props +}: ComponentProps<"button"> & { options: LayoutTab[]; placeholder?: React.ReactNode }) { + const [open, setOpen] = useState(false); + const { closeOnRedirect } = useSidebar(); + const pathname = usePathname(); + const selected = useMemo( + () => options.findLast((item) => isLayoutTabActive(item, pathname)), + [options, pathname], + ); + const sections = useMemo(() => groupTabs(options), [options]); + + const onClick = () => { + closeOnRedirect.current = false; + setOpen(false); + }; + + const item = selected ? ( + <> +
{selected.icon}
+
+

{selected.title}

+

+ {selected.description} +

+
+ + ) : ( + placeholder + ); + + return ( + + {item && ( + + {item} + + + )} + + {sections.map((section) => ( + +

+ {section.title} +

+ {section.options.map((option) => { + const isActive = selected && option.url === selected.url; + if (!isActive && option.unlisted) return null; + + return ( + +
+ {option.icon} +
+
+

{option.title}

+

+ {option.description} +

+
+ + + ); + })} +
+ ))} +
+
+ ); +} diff --git a/src/lib/llms.ts b/src/lib/llms.ts index 8470d8ec..9b89492a 100644 --- a/src/lib/llms.ts +++ b/src/lib/llms.ts @@ -25,10 +25,18 @@ export const llmsSectionConfigs = { label: "Expo", urlPrefix: "/docs/expo", }, + kmp: { + label: "KMP", + urlPrefix: "/docs/kmp", + }, unity: { label: "Unity", urlPrefix: "/docs/unity", }, + web: { + label: "Web", + urlPrefix: "/docs/web", + }, "react-native": { label: "React Native", urlPrefix: "/docs/react-native", @@ -90,7 +98,9 @@ function getExamplePathForSection(section: string): string { android: "/docs/android/guides/advanced/game-controller-support.md", flutter: "/docs/flutter/guides/advanced/game-controller-support.md", expo: "/docs/expo/guides/advanced/game-controller-support.md", + kmp: "/docs/kmp/guides/platform-differences.md", unity: "/docs/unity/guides/game-controller-input.md", + web: "/docs/web/guides/server-side-gating.md", "react-native": "/docs/react-native/guides/advanced/game-controller-support.md", dashboard: "/docs/dashboard/dashboard-settings/overview-settings-apple-search-ads.md", agents: "/docs/agents/create-an-agent.md", @@ -107,7 +117,7 @@ export function buildLLMSummaryTextFromPages(pages: LLMPageLike[], section?: str // Add Superwall description lines.push( - "Superwall is a platform for remotely configuring and A/B testing mobile app paywalls — no app update required. It provides SDKs for iOS, Android, Flutter, Expo, Unity, and React Native, plus a dashboard for managing paywall presentation, experiments, and subscription analytics.", + "Superwall is a platform for remotely configuring and A/B testing mobile app paywalls — no app update required. It provides SDKs for iOS, Android, Flutter, Expo, KMP, Unity, Web, and React Native, plus a dashboard for managing paywall presentation, experiments, and subscription analytics.", "", ); diff --git a/src/lib/og.shared.ts b/src/lib/og.shared.ts index 62de41a4..c6ed0729 100644 --- a/src/lib/og.shared.ts +++ b/src/lib/og.shared.ts @@ -25,11 +25,13 @@ const SECTION_LABELS: Record = { flutter: "Flutter", integrations: "Integrations", ios: "iOS", + kmp: "KMP", legacy: "Legacy", "react-native": "React Native", sdk: "SDKs", support: "Support", unity: "Unity", + web: "Web", "web-checkout": "Web Checkout", }; diff --git a/src/lib/sdk-tabs.ts b/src/lib/sdk-tabs.ts new file mode 100644 index 00000000..a9f467e0 --- /dev/null +++ b/src/lib/sdk-tabs.ts @@ -0,0 +1,31 @@ +import type { LayoutTab } from "fumadocs-ui/layouts/shared"; + +/** + * Tabs that belong to the grouped SDK navigation: a dropdown on the desktop navbar, + * an "SDKs" section at the end of the mobile sidebar dropdown. + */ +const SDK_TAB_LABELS: Record = { + ios: "iOS", + android: "Android", + expo: "Expo", + flutter: "Flutter", + kmp: "KMP", + unity: "Unity", + web: "Web", + "react-native": "React Native", + community: "Community", +}; + +const SDK_TAB_SLUGS = new Set(Object.keys(SDK_TAB_LABELS)); + +function getTabPrefix(url: string) { + return url.replace(/^\/docs\//, "").split("/")[0] ?? ""; +} + +export function isSdkTab(tab: LayoutTab) { + return SDK_TAB_SLUGS.has(getTabPrefix(tab.url)); +} + +export function getSdkTabLabel(tab: LayoutTab) { + return SDK_TAB_LABELS[getTabPrefix(tab.url)] ?? String(tab.title); +} diff --git a/src/lib/structured-data.ts b/src/lib/structured-data.ts index 35dc4080..3a69e929 100644 --- a/src/lib/structured-data.ts +++ b/src/lib/structured-data.ts @@ -54,7 +54,9 @@ const SECTION_NAMES_BY_SLUG: Record = { android: "Android SDK", expo: "Expo SDK", flutter: "Flutter SDK", + kmp: "KMP SDK", unity: "Unity SDK", + web: "Web SDK", "react-native": "React Native SDK", community: "Community SDKs", }; diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index a8e45ada..e9a798a1 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -22,6 +22,8 @@ import { Route as LlmsFullChar123sectionChar125DottxtRouteImport } from './route import { Route as SplatRouteImport } from './routes/$' import { Route as IndexRouteImport } from './routes/index' import { Route as SdkIndexRouteImport } from './routes/sdk/index' +import { Route as WebLlmsDottxtRouteImport } from './routes/web/llms[.]txt' +import { Route as WebLlmsFullDottxtRouteImport } from './routes/web/llms-full[.]txt' import { Route as WebCheckoutLlmsDottxtRouteImport } from './routes/web-checkout/llms[.]txt' import { Route as WebCheckoutLlmsFullDottxtRouteImport } from './routes/web-checkout/llms-full[.]txt' import { Route as UnityLlmsDottxtRouteImport } from './routes/unity/llms[.]txt' @@ -32,6 +34,8 @@ import { Route as SdkSplatRouteImport } from './routes/sdk/$' import { Route as ReactNativeLlmsDottxtRouteImport } from './routes/react-native/llms[.]txt' import { Route as ReactNativeLlmsFullDottxtRouteImport } from './routes/react-native/llms-full[.]txt' import { Route as LlmsDotmdxDocsRouteImport } from './routes/llms[.]mdx.docs' +import { Route as KmpLlmsDottxtRouteImport } from './routes/kmp/llms[.]txt' +import { Route as KmpLlmsFullDottxtRouteImport } from './routes/kmp/llms-full[.]txt' import { Route as IosLlmsDottxtRouteImport } from './routes/ios/llms[.]txt' import { Route as IosLlmsFullDottxtRouteImport } from './routes/ios/llms-full[.]txt' import { Route as IntegrationsLlmsDottxtRouteImport } from './routes/integrations/llms[.]txt' @@ -119,6 +123,16 @@ const SdkIndexRoute = SdkIndexRouteImport.update({ path: '/sdk/', getParentRoute: () => rootRouteImport, } as any) +const WebLlmsDottxtRoute = WebLlmsDottxtRouteImport.update({ + id: '/web/llms.txt', + path: '/web/llms.txt', + getParentRoute: () => rootRouteImport, +} as any) +const WebLlmsFullDottxtRoute = WebLlmsFullDottxtRouteImport.update({ + id: '/web/llms-full.txt', + path: '/web/llms-full.txt', + getParentRoute: () => rootRouteImport, +} as any) const WebCheckoutLlmsDottxtRoute = WebCheckoutLlmsDottxtRouteImport.update({ id: '/web-checkout/llms.txt', path: '/web-checkout/llms.txt', @@ -171,6 +185,16 @@ const LlmsDotmdxDocsRoute = LlmsDotmdxDocsRouteImport.update({ path: '/llms.mdx/docs', getParentRoute: () => rootRouteImport, } as any) +const KmpLlmsDottxtRoute = KmpLlmsDottxtRouteImport.update({ + id: '/kmp/llms.txt', + path: '/kmp/llms.txt', + getParentRoute: () => rootRouteImport, +} as any) +const KmpLlmsFullDottxtRoute = KmpLlmsFullDottxtRouteImport.update({ + id: '/kmp/llms-full.txt', + path: '/kmp/llms-full.txt', + getParentRoute: () => rootRouteImport, +} as any) const IosLlmsDottxtRoute = IosLlmsDottxtRouteImport.update({ id: '/ios/llms.txt', path: '/ios/llms.txt', @@ -297,6 +321,8 @@ export interface FileRoutesByFullPath { '/integrations/llms.txt': typeof IntegrationsLlmsDottxtRoute '/ios/llms-full.txt': typeof IosLlmsFullDottxtRoute '/ios/llms.txt': typeof IosLlmsDottxtRoute + '/kmp/llms-full.txt': typeof KmpLlmsFullDottxtRoute + '/kmp/llms.txt': typeof KmpLlmsDottxtRoute '/llms.mdx/docs': typeof LlmsDotmdxDocsRouteWithChildren '/react-native/llms-full.txt': typeof ReactNativeLlmsFullDottxtRoute '/react-native/llms.txt': typeof ReactNativeLlmsDottxtRoute @@ -307,6 +333,8 @@ export interface FileRoutesByFullPath { '/unity/llms.txt': typeof UnityLlmsDottxtRoute '/web-checkout/llms-full.txt': typeof WebCheckoutLlmsFullDottxtRoute '/web-checkout/llms.txt': typeof WebCheckoutLlmsDottxtRoute + '/web/llms-full.txt': typeof WebLlmsFullDottxtRoute + '/web/llms.txt': typeof WebLlmsDottxtRoute '/sdk/': typeof SdkIndexRoute '/api/raw/$': typeof ApiRawSplatRoute '/llms.mdx/docs/$': typeof LlmsDotmdxDocsSplatRoute @@ -341,6 +369,8 @@ export interface FileRoutesByTo { '/integrations/llms.txt': typeof IntegrationsLlmsDottxtRoute '/ios/llms-full.txt': typeof IosLlmsFullDottxtRoute '/ios/llms.txt': typeof IosLlmsDottxtRoute + '/kmp/llms-full.txt': typeof KmpLlmsFullDottxtRoute + '/kmp/llms.txt': typeof KmpLlmsDottxtRoute '/react-native/llms-full.txt': typeof ReactNativeLlmsFullDottxtRoute '/react-native/llms.txt': typeof ReactNativeLlmsDottxtRoute '/sdk/$': typeof SdkSplatRoute @@ -350,6 +380,8 @@ export interface FileRoutesByTo { '/unity/llms.txt': typeof UnityLlmsDottxtRoute '/web-checkout/llms-full.txt': typeof WebCheckoutLlmsFullDottxtRoute '/web-checkout/llms.txt': typeof WebCheckoutLlmsDottxtRoute + '/web/llms-full.txt': typeof WebLlmsFullDottxtRoute + '/web/llms.txt': typeof WebLlmsDottxtRoute '/sdk': typeof SdkIndexRoute '/api/raw/$': typeof ApiRawSplatRoute '/llms.mdx/docs/$': typeof LlmsDotmdxDocsSplatRoute @@ -385,6 +417,8 @@ export interface FileRoutesById { '/integrations/llms.txt': typeof IntegrationsLlmsDottxtRoute '/ios/llms-full.txt': typeof IosLlmsFullDottxtRoute '/ios/llms.txt': typeof IosLlmsDottxtRoute + '/kmp/llms-full.txt': typeof KmpLlmsFullDottxtRoute + '/kmp/llms.txt': typeof KmpLlmsDottxtRoute '/llms.mdx/docs': typeof LlmsDotmdxDocsRouteWithChildren '/react-native/llms-full.txt': typeof ReactNativeLlmsFullDottxtRoute '/react-native/llms.txt': typeof ReactNativeLlmsDottxtRoute @@ -395,6 +429,8 @@ export interface FileRoutesById { '/unity/llms.txt': typeof UnityLlmsDottxtRoute '/web-checkout/llms-full.txt': typeof WebCheckoutLlmsFullDottxtRoute '/web-checkout/llms.txt': typeof WebCheckoutLlmsDottxtRoute + '/web/llms-full.txt': typeof WebLlmsFullDottxtRoute + '/web/llms.txt': typeof WebLlmsDottxtRoute '/sdk/': typeof SdkIndexRoute '/api/raw/$': typeof ApiRawSplatRoute '/llms.mdx/docs/$': typeof LlmsDotmdxDocsSplatRoute @@ -431,6 +467,8 @@ export interface FileRouteTypes { | '/integrations/llms.txt' | '/ios/llms-full.txt' | '/ios/llms.txt' + | '/kmp/llms-full.txt' + | '/kmp/llms.txt' | '/llms.mdx/docs' | '/react-native/llms-full.txt' | '/react-native/llms.txt' @@ -441,6 +479,8 @@ export interface FileRouteTypes { | '/unity/llms.txt' | '/web-checkout/llms-full.txt' | '/web-checkout/llms.txt' + | '/web/llms-full.txt' + | '/web/llms.txt' | '/sdk/' | '/api/raw/$' | '/llms.mdx/docs/$' @@ -475,6 +515,8 @@ export interface FileRouteTypes { | '/integrations/llms.txt' | '/ios/llms-full.txt' | '/ios/llms.txt' + | '/kmp/llms-full.txt' + | '/kmp/llms.txt' | '/react-native/llms-full.txt' | '/react-native/llms.txt' | '/sdk/$' @@ -484,6 +526,8 @@ export interface FileRouteTypes { | '/unity/llms.txt' | '/web-checkout/llms-full.txt' | '/web-checkout/llms.txt' + | '/web/llms-full.txt' + | '/web/llms.txt' | '/sdk' | '/api/raw/$' | '/llms.mdx/docs/$' @@ -518,6 +562,8 @@ export interface FileRouteTypes { | '/integrations/llms.txt' | '/ios/llms-full.txt' | '/ios/llms.txt' + | '/kmp/llms-full.txt' + | '/kmp/llms.txt' | '/llms.mdx/docs' | '/react-native/llms-full.txt' | '/react-native/llms.txt' @@ -528,6 +574,8 @@ export interface FileRouteTypes { | '/unity/llms.txt' | '/web-checkout/llms-full.txt' | '/web-checkout/llms.txt' + | '/web/llms-full.txt' + | '/web/llms.txt' | '/sdk/' | '/api/raw/$' | '/llms.mdx/docs/$' @@ -563,6 +611,8 @@ export interface RootRouteChildren { IntegrationsLlmsDottxtRoute: typeof IntegrationsLlmsDottxtRoute IosLlmsFullDottxtRoute: typeof IosLlmsFullDottxtRoute IosLlmsDottxtRoute: typeof IosLlmsDottxtRoute + KmpLlmsFullDottxtRoute: typeof KmpLlmsFullDottxtRoute + KmpLlmsDottxtRoute: typeof KmpLlmsDottxtRoute LlmsDotmdxDocsRoute: typeof LlmsDotmdxDocsRouteWithChildren ReactNativeLlmsFullDottxtRoute: typeof ReactNativeLlmsFullDottxtRoute ReactNativeLlmsDottxtRoute: typeof ReactNativeLlmsDottxtRoute @@ -573,6 +623,8 @@ export interface RootRouteChildren { UnityLlmsDottxtRoute: typeof UnityLlmsDottxtRoute WebCheckoutLlmsFullDottxtRoute: typeof WebCheckoutLlmsFullDottxtRoute WebCheckoutLlmsDottxtRoute: typeof WebCheckoutLlmsDottxtRoute + WebLlmsFullDottxtRoute: typeof WebLlmsFullDottxtRoute + WebLlmsDottxtRoute: typeof WebLlmsDottxtRoute SdkIndexRoute: typeof SdkIndexRoute ApiRawSplatRoute: typeof ApiRawSplatRoute } @@ -670,6 +722,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof SdkIndexRouteImport parentRoute: typeof rootRouteImport } + '/web/llms.txt': { + id: '/web/llms.txt' + path: '/web/llms.txt' + fullPath: '/web/llms.txt' + preLoaderRoute: typeof WebLlmsDottxtRouteImport + parentRoute: typeof rootRouteImport + } + '/web/llms-full.txt': { + id: '/web/llms-full.txt' + path: '/web/llms-full.txt' + fullPath: '/web/llms-full.txt' + preLoaderRoute: typeof WebLlmsFullDottxtRouteImport + parentRoute: typeof rootRouteImport + } '/web-checkout/llms.txt': { id: '/web-checkout/llms.txt' path: '/web-checkout/llms.txt' @@ -740,6 +806,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LlmsDotmdxDocsRouteImport parentRoute: typeof rootRouteImport } + '/kmp/llms.txt': { + id: '/kmp/llms.txt' + path: '/kmp/llms.txt' + fullPath: '/kmp/llms.txt' + preLoaderRoute: typeof KmpLlmsDottxtRouteImport + parentRoute: typeof rootRouteImport + } + '/kmp/llms-full.txt': { + id: '/kmp/llms-full.txt' + path: '/kmp/llms-full.txt' + fullPath: '/kmp/llms-full.txt' + preLoaderRoute: typeof KmpLlmsFullDottxtRouteImport + parentRoute: typeof rootRouteImport + } '/ios/llms.txt': { id: '/ios/llms.txt' path: '/ios/llms.txt' @@ -920,6 +1000,8 @@ const rootRouteChildren: RootRouteChildren = { IntegrationsLlmsDottxtRoute: IntegrationsLlmsDottxtRoute, IosLlmsFullDottxtRoute: IosLlmsFullDottxtRoute, IosLlmsDottxtRoute: IosLlmsDottxtRoute, + KmpLlmsFullDottxtRoute: KmpLlmsFullDottxtRoute, + KmpLlmsDottxtRoute: KmpLlmsDottxtRoute, LlmsDotmdxDocsRoute: LlmsDotmdxDocsRouteWithChildren, ReactNativeLlmsFullDottxtRoute: ReactNativeLlmsFullDottxtRoute, ReactNativeLlmsDottxtRoute: ReactNativeLlmsDottxtRoute, @@ -930,6 +1012,8 @@ const rootRouteChildren: RootRouteChildren = { UnityLlmsDottxtRoute: UnityLlmsDottxtRoute, WebCheckoutLlmsFullDottxtRoute: WebCheckoutLlmsFullDottxtRoute, WebCheckoutLlmsDottxtRoute: WebCheckoutLlmsDottxtRoute, + WebLlmsFullDottxtRoute: WebLlmsFullDottxtRoute, + WebLlmsDottxtRoute: WebLlmsDottxtRoute, SdkIndexRoute: SdkIndexRoute, ApiRawSplatRoute: ApiRawSplatRoute, } diff --git a/src/routes/$.tsx b/src/routes/$.tsx index d746f0bf..789dfcdf 100644 --- a/src/routes/$.tsx +++ b/src/routes/$.tsx @@ -1,5 +1,11 @@ import { createFileRoute, notFound } from "@tanstack/react-router"; import { DocsLayout } from "fumadocs-ui/layouts/notebook"; +import { + SidebarCollapseTrigger, + SidebarProvider, + SidebarTrigger, + useSidebar, +} from "fumadocs-ui/layouts/notebook/slots/sidebar"; import { getLayoutTabs, type LayoutTab } from "fumadocs-ui/layouts/shared"; import type { Root as PageTreeRoot } from "fumadocs-core/page-tree"; import { createServerFn } from "@tanstack/react-start"; @@ -16,6 +22,7 @@ import { Feedback } from "@/components/feedback/client"; import type { ActionResponse, PageFeedback } from "@/components/feedback/schema"; import { LoginStatusProvider } from "@/components/LoginStatusContext"; import { DocsHeader } from "@/components/DocsHeader"; +import { DocsSidebar } from "@/components/DocsSidebar"; import { buildCanonicalUrl, buildSocialImageUrl, @@ -274,7 +281,16 @@ function Page() { prefetch: true, footer: , }} - slots={{ header: DocsHeader }} + slots={{ + header: DocsHeader, + sidebar: { + provider: SidebarProvider, + root: DocsSidebar, + trigger: SidebarTrigger, + collapseTrigger: SidebarCollapseTrigger, + useSidebar, + }, + }} > {clientLoader.useContent(data.path, data)} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 10f25023..0778fb98 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -146,12 +146,24 @@ const sdkCards: DocCard[] = [ href: buildDocsPath("flutter"), icon: , }, + { + title: "KMP (Beta)", + description: "Integrate Superwall into your Kotlin Multiplatform app.", + href: buildDocsPath("kmp"), + icon: , + }, { title: "Unity (Beta)", description: "Integrate Superwall into your Unity mobile game.", href: buildDocsPath("unity"), icon: , }, + { + title: "Web (Beta)", + description: "Integrate Superwall into your web app with JavaScript or React.", + href: buildDocsPath("web"), + icon: , + }, { title: "React Native (Legacy)", description: "Legacy SDK for React Native. Migrate to the Expo SDK for new projects.", diff --git a/src/routes/kmp/llms-full[.]txt.ts b/src/routes/kmp/llms-full[.]txt.ts new file mode 100644 index 00000000..41bfafb1 --- /dev/null +++ b/src/routes/kmp/llms-full[.]txt.ts @@ -0,0 +1,10 @@ +import { buildLLMFullResponseForSection } from "@/lib/llms"; +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/kmp/llms-full.txt")({ + server: { + handlers: { + GET: () => buildLLMFullResponseForSection("kmp"), + }, + }, +}); diff --git a/src/routes/kmp/llms[.]txt.ts b/src/routes/kmp/llms[.]txt.ts new file mode 100644 index 00000000..580069ab --- /dev/null +++ b/src/routes/kmp/llms[.]txt.ts @@ -0,0 +1,10 @@ +import { buildLLMSummaryResponseForSection } from "@/lib/llms"; +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/kmp/llms.txt")({ + server: { + handlers: { + GET: () => buildLLMSummaryResponseForSection("kmp"), + }, + }, +}); diff --git a/src/routes/web/llms-full[.]txt.ts b/src/routes/web/llms-full[.]txt.ts new file mode 100644 index 00000000..06634e0f --- /dev/null +++ b/src/routes/web/llms-full[.]txt.ts @@ -0,0 +1,10 @@ +import { buildLLMFullResponseForSection } from "@/lib/llms"; +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/web/llms-full.txt")({ + server: { + handlers: { + GET: () => buildLLMFullResponseForSection("web"), + }, + }, +}); diff --git a/src/routes/web/llms[.]txt.ts b/src/routes/web/llms[.]txt.ts new file mode 100644 index 00000000..e5c2cf0d --- /dev/null +++ b/src/routes/web/llms[.]txt.ts @@ -0,0 +1,10 @@ +import { buildLLMSummaryResponseForSection } from "@/lib/llms"; +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/web/llms.txt")({ + server: { + handlers: { + GET: () => buildLLMSummaryResponseForSection("web"), + }, + }, +});