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
32 changes: 26 additions & 6 deletions app/(docs)/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { APIPage } from '@/components/openapi/api-page';
import { source } from '@/lib/source';
import { getMDXComponents } from '@/mdx-components';
import { createRelativeLink } from 'fumadocs-ui/mdx';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
} from 'fumadocs-ui/page';
} from 'fumadocs-ui/layouts/notebook/page';
import { createRelativeLink } from 'fumadocs-ui/mdx';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';

Expand All @@ -15,15 +16,27 @@ export default async function Page(props: PageProps<'/[[...slug]]'>) {
const page = source.getPage(params.slug);
if (!page) notFound();

if (page.data.type === 'openapi') {
return (
<DocsPage full>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<APIPage {...page.data.getAPIPageProps()} showDescription={false} />
</DocsBody>
</DocsPage>
);
}

const MDX = page.data.body;

return (
<DocsPage
toc={page.data.toc}
full={page.data.full}
breadcrumb={{ enabled: page.url.includes('/api-reference') }}
breadcrumb={{ enabled: false }}
>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsTitle>{page.data.pageTitle || page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<MDX
Expand All @@ -41,14 +54,21 @@ export async function generateStaticParams() {
}

export async function generateMetadata(
props: PageProps<'/[[...slug]]'>
props: PageProps<'/[[...slug]]'>,
): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();

if (page.data.type === 'openapi') {
return {
title: page.data.title,
description: page.data.description,
};
}

return {
title: page.data.title,
title: page.data.pageTitle || page.data.title,
description: page.data.description,
};
}
4 changes: 1 addition & 3 deletions app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@

#nd-subnav {
> div {
@apply border-b-0! h-16!;
@apply border-b-0!;
}

@apply layout:[--fd-header-height:64px]!;
}
13 changes: 10 additions & 3 deletions app/llms-full.txt/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@ import { getLlmText, source } from '@/lib/source';
export const revalidate = false;

export async function GET() {
const scan = source.getPages().map(getLlmText);
const scanned = await Promise.all(scan);
const pages = source.getPages();
const scanned = (await Promise.all(pages.map(getLlmText))).filter(Boolean);

return new Response(scanned.join('\n\n'));
const openapiPage = pages.find((page) => page.data.type === 'openapi');
if (openapiPage && openapiPage.data.type === 'openapi') {
scanned.push(`# OpenAPI Specification

${JSON.stringify(openapiPage.data.getSchema().bundled, null, 2)}`);
}

return new Response(scanned.join('\n\n\n'));
}
14 changes: 8 additions & 6 deletions components/page-select.tsx → components/card-link-select.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
'use client';

import { cn } from '@/lib/utils';
import Link from 'fumadocs-core/link';
import { cn } from 'fumadocs-ui/utils/cn';
import { usePathname } from 'next/navigation';

type PageSelectProps = {
type CardLinkSelectProps = {
pages: {
href: string;
title: string;
description?: string;
}[];
};

export function PageSelect({ pages }: PageSelectProps) {
export function CardLinkSelect({ pages }: CardLinkSelectProps) {
const pathname = usePathname();

return (
Expand All @@ -26,9 +26,11 @@ export function PageSelect({ pages }: PageSelectProps) {
href={page.href}
data-card
className={cn(
'bg-fd-card/80 text-fd-card-foreground hover:bg-fd-accent/70 @max-lg:col-span-full block rounded-xl border p-4 shadow-md transition-colors',
active &&
'bg-fd-primary/10 border-fd-primary/70 hover:bg-fd-primary/10'
'bg-fd-card text-fd-card-foreground hover:bg-fd-accent/60 @max-lg:col-span-full block rounded-xl border p-4 shadow-md transition-colors',
{
'bg-fd-primary/10 border-fd-primary/70 hover:bg-fd-primary/10':
active,
},
)}
>
<h3 className="not-prose mb-1 text-sm font-medium">{page.title}</h3>
Expand Down
50 changes: 50 additions & 0 deletions components/icons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
export function StripeIcon() {
return (
<span className="flex size-5 shrink-0 items-center justify-center rounded-sm bg-[#635bff] [&>svg]:size-3! [&>svg]:fill-white!">
<svg
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
style={{ fill: '#635bff' }}
>
<path d="M13.976 9.15c-2.172-.806-3.356-1.426-3.356-2.409 0-.831.683-1.305 1.901-1.305 2.227 0 4.515.858 6.09 1.631l.89-5.494C18.252.975 15.697 0 12.165 0 9.667 0 7.589.654 6.104 1.872 4.56 3.147 3.757 4.992 3.757 7.218c0 4.039 2.467 5.76 6.476 7.219 2.585.92 3.445 1.574 3.445 2.583 0 .98-.84 1.545-2.354 1.545-1.875 0-4.965-.921-6.99-2.109l-.9 5.555C5.175 22.99 8.385 24 11.714 24c2.641 0 4.843-.624 6.328-1.813 1.664-1.305 2.525-3.236 2.525-5.732 0-4.128-2.524-5.851-6.594-7.305h.003z" />
</svg>
</span>
);
}

export function LemonSqueezyIcon() {
return (
<span className="flex size-5 shrink-0 items-center justify-center rounded-sm bg-[#5423e7] [&>svg]:size-3!">
<svg viewBox="0 0 21 28" style={{ fill: '#FFC233' }}>
<path
fillRule="evenodd"
d="m6.92882 17.1856 7.51128 3.4727c.931.4306 1.5881 1.1533 1.943 1.9823.8976 2.0993-.3292 4.2463-2.255 5.0185-1.9262.7718-3.979.2751-4.91242-1.908l-3.26891-7.6645c-.25331-.5941.38303-1.1779.98205-.901Zm.45024-2.248 7.75364-2.931c2.5769-.9741 5.3918.869 5.3538 3.547-.0006.035-.0012.0699-.0021.1052-.0557 2.6078-2.7923 4.3606-5.3126 3.438l-7.7854-2.8495c-.62104-.2272-.62563-1.076-.00734-1.3097Zm-.43407-1.0152 7.62211-3.2387c2.5328-1.07634 3.1756-4.30675 1.1919-6.17327a9.026257 9.026257 0 0 0-.0783-.07315c-1.9449-1.80521-5.1599-1.16961-6.26712 1.20811L5.99323 12.9915c-.2729.5858.34387 1.1891.95176.9309Zm-1.9615-1.2798 2.77116-7.59845c.34357-.94215.27993-1.90295-.07526-2.73195C6.77994.21378 4.34409-.463579 2.41853.309741.493284 1.08336-.594621 2.84029.340622 5.02253L3.63095 12.6787c.25515.5933 1.13166.5699 1.35254-.0361Z"
clipRule="evenodd"
/>
</svg>
</span>
);
}

export function PolarIcon() {
return (
<span className="flex size-5 shrink-0 items-center justify-center rounded-sm bg-[#070708] [&>svg]:size-3.5!">
{' '}
<svg fill="none" viewBox="0 0 300 300">
<g clipPath="url(#polar_sh_dark__a)">
<path
fill="#fff"
fillRule="evenodd"
d="M66.428 274.26c68.448 46.333 161.497 28.406 207.83-40.041 46.335-68.448 28.408-161.497-40.04-207.83C165.77-19.946 72.721-2.019 26.388 66.428-19.948 134.878-2.02 227.928 66.427 274.26ZM47.956 116.67c-17.119 52.593-11.412 105.223 11.29 139.703C18.04 217.361 7.275 150.307 36.943 92.318c18.971-37.082 50.622-62.924 85.556-73.97-31.909 18.363-59.945 53.466-74.544 98.322Zm127.391 166.467c36.03-10.531 68.864-36.752 88.338-74.815 29.416-57.497 19.083-123.905-21.258-163.055 21.793 34.496 27.046 86.275 10.204 138.02-15.016 46.134-44.246 81.952-77.284 99.85Zm8.28-16.908c24.318-20.811 44.389-55.625 53.308-97.439 14.098-66.097-4.384-127.592-41.823-148.113 19.858 26.718 29.91 78.613 23.712 136.656-4.739 44.391-18.01 83.26-35.197 108.896ZM63.717 131.844c-14.201 66.586 4.66 128.501 42.657 148.561-20.378-26.396-30.777-78.891-24.498-137.694 4.661-43.657 17.574-81.974 34.349-107.614-23.957 20.886-43.687 55.392-52.507 96.747Zm136.117 17.717c1.074 67.912-20.244 123.317-47.612 123.748-27.369.433-50.425-54.27-51.498-122.182-1.073-67.913 20.244-123.318 47.613-123.75 27.368-.432 50.425 54.271 51.497 122.184Z"
clipRule="evenodd"
/>
</g>
<defs>
<clipPath id="polar_sh_dark__a">
<path fill="#fff" d="M0 0h300v300H0z" />
</clipPath>
</defs>
</svg>
</span>
);
}
2 changes: 1 addition & 1 deletion components/mdx/license-token.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ To make this possible, Keyforge can issue a signed **license token** that is ver

There is a client SDK available for JavaScript, but license tokens can be used in any programming language that supports JWTs.

<PageSelect
<CardLinkSelect
pages={[
{
href: '/addons/license-token-no-sdk',
Expand Down
75 changes: 64 additions & 11 deletions components/mdx/payments.mdx
Original file line number Diff line number Diff line change
@@ -1,27 +1,80 @@
<section id="page-header">
<section id="setup-page-header">

<Callout>
Keyforge uses Stripe restricted API keys with only the necessary permissions.
The secret keys are securely stored and encrypted with AES-256.
</Callout>
import { StripeIcon, LemonSqueezyIcon, PolarIcon } from '@/components/icons';

Keyforge can integrate with Stripe for automatic license generation. No code or webhook setup needed.
Keyforge can integrate with various payment providers to automatically generate, upgrade, and renew licenses. No code or webhook setup needed.

<PageSelect
<CardLinkSelect
pages={[
{
href: '/addons/payments-one-time',
href: '/addons/payment/setup/one-time',
title: 'One-time purchases',
description:
'Accept one-time payments and automatically generate licenses.',
'Accept one-time payments and automatically create licenses.',
},
{
href: '/addons/payments-subscription',
href: '/addons/payment/setup/subscription',
title: 'Subscriptions',
description:
'Accept recurring payments and generate subscription licenses.',
'Accept recurring payments and link licenses with subscriptions.',
},
]}
/>

Before starting, select the payment provider you're using for a personalized guide.

<QueryTabs
param="provider"
defaultValue="stripe"
items={[
{
value: 'stripe',
label: (
<>
<StripeIcon />
Stripe
</>
),
},
{
value: 'lmsqzy',
label: (
<>
<LemonSqueezyIcon />
Lemon Squeezy
</>
),
},
{
value: 'polar',
label: (
<>
<PolarIcon />
Polar
</>
),
},
]}
/>

<QueryOutlet param="provider" defaultValue="stripe">
<QueryOutletCase value="stripe">
<Callout>
Keyforge uses Stripe restricted API keys with only the necessary permissions. Secret keys are securely stored and encrypted with AES-256.
</Callout>
</QueryOutletCase>

<QueryOutletCase value="lmsqzy">
<Callout>
All Lemon Squeezy API keys are securely stored and encrypted with AES-256.
</Callout>
</QueryOutletCase>

<QueryOutletCase value="polar">
<Callout>
Keyforge uses Polar Organization Access Tokens with only the necessary permissions. Tokens are securely stored and encrypted with AES-256.
</Callout>
</QueryOutletCase>
</QueryOutlet>

</section>
2 changes: 1 addition & 1 deletion components/mdx/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ You can set up Keyforge in your project within minutes. This guide will walk you

Choose the appropriate quickstart guide for your use case:

<PageSelect
<CardLinkSelect
pages={[
{
href: '/quickstart',
Expand Down
54 changes: 54 additions & 0 deletions components/query-outlet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use client';

import { useSearchParams } from 'next/navigation';
import { Tabs as TabsPrimitive } from 'radix-ui';
import { Suspense } from 'react';

type QueryOutletProps = {
param: string;
defaultValue?: string;
children: React.ReactNode;
};

export function QueryOutlet(props: QueryOutletProps) {
return (
<Suspense
fallback={
<Outlet activeValue={props.defaultValue ?? ''}>{props.children}</Outlet>
}
>
<Wrapper {...props} />
</Suspense>
);
}

function Wrapper(props: QueryOutletProps) {
const searchParams = useSearchParams();

return (
<Outlet
activeValue={searchParams.get(props.param) ?? props.defaultValue ?? ''}
>
{props.children}
</Outlet>
);
}

function Outlet(props: { activeValue: string; children: React.ReactNode }) {
return (
<TabsPrimitive.Root value={props.activeValue}>
{props.children}
</TabsPrimitive.Root>
);
}

export function QueryOutletCase(props: {
value: string;
children: React.ReactNode;
}) {
return (
<TabsPrimitive.Content value={props.value}>
{props.children}
</TabsPrimitive.Content>
);
}
Loading