Skip to content

feat: content re-organization #1772

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 8, 2025
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
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@
"search.defaultViewMode": "tree",
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[mdx]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
2 changes: 1 addition & 1 deletion examples/01-basic/02-block-objects/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function App() {
<div>Document JSON:</div>
<div className={"item bordered"}>
<pre>
<code>{JSON.stringify(blocks, null, 2)}</code>
<code>{JSON.stringify(editor.document, null, 2)}</code>
</pre>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BlockNoteView } from "@blocknote/mantine";
import "@blocknote/mantine/style.css";
import { useCreateBlockNote } from "@blocknote/react";

import { ToggleBlock } from "./Toggle.js";
import { ToggleBlock } from "./Toggle";

// Our schema with block specs, which contain the configs and implementations for
// blocks that we want our editor to use.
Expand Down
6 changes: 5 additions & 1 deletion fumadocs/app/examples/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ export default function Layout({ children }: { children: ReactNode }) {
for (const page of category.children) {
if (page.type === "page" && page.$ref?.file) {
const [exampleGroupName, exampleName] = page.$ref.file.split("/");
const exampleData = getExampleData(exampleGroupName, exampleName);

const exampleData = getExampleData(
exampleGroupName,
exampleName.split(".mdx")[0],
);

page.name = (
<span>
Expand Down
1 change: 1 addition & 0 deletions fumadocs/app/global.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@import "tailwindcss";
@import "fumadocs-ui/css/vitepress.css";
@import "fumadocs-ui/css/preset.css";
@import "fumadocs-twoslash/twoslash.css";

@source ".";
@source "../components";
Expand Down
30 changes: 21 additions & 9 deletions fumadocs/app/pricing/tiers.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import { useSession } from "@/util/auth-client";
import { authClient, useSession } from "@/util/auth-client";
import { CheckIcon } from "@heroicons/react/20/solid";
import { track } from "@vercel/analytics";
import classNames from "classnames";
Expand Down Expand Up @@ -55,15 +55,12 @@ function TierDescription({ tier }: { tier: Tier }) {

function TierCTAButton({ tier }: { tier: Tier }) {
const { data: session } = useSession();
let href = "/signup";
let text = "Sign up";

if (session) {
if (session.planType === "free") {
href = `/api/auth/checkout/${tier.id}`;
text = "Buy now";
} else {
href = "/api/auth/portal";
text =
session.planType === tier.id
? "Manage subscription"
Expand All @@ -72,17 +69,32 @@ function TierCTAButton({ tier }: { tier: Tier }) {
}
return (
<a
href={tier.href ?? href}
onClick={async (e) => {
if (!session) {
return;
}

if (session.planType === "free") {
track("Signup", { tier: tier.id });
e.preventDefault();
e.stopPropagation();
await authClient.checkout({
slug: tier.id,
});
} else {
e.preventDefault();
e.stopPropagation();
await authClient.customer.portal();
}
}}
href={tier.href ?? (session ? undefined : "/signup")}
aria-describedby={tier.id}
className={classNames(
tier.mostPopular
? "text-fd-foreground bg-indigo-600 shadow-sm hover:bg-indigo-500"
: "text-indigo-600 ring-1 ring-inset ring-indigo-600 hover:text-indigo-500 hover:ring-indigo-500",
"mt-8 block rounded-md px-3 py-2 text-center text-sm font-semibold leading-6 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600",
"mt-8 block cursor-pointer rounded-md px-3 py-2 text-center text-sm font-semibold leading-6 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600",
)}
onClick={() => {
track("Signup", { tier: tier.id });
}}
>
{tier.id === "enterprise" ? "Get in touch" : text}
</a>
Expand Down
103 changes: 49 additions & 54 deletions fumadocs/auth.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { polar } from "@polar-sh/better-auth";
import { polar, checkout, portal, webhooks } from "@polar-sh/better-auth";
import { Polar } from "@polar-sh/sdk";
import * as Sentry from "@sentry/nextjs";
import { betterAuth } from "better-auth";
Expand Down Expand Up @@ -199,61 +199,56 @@ export const auth = betterAuth({
client: polarClient,
// Enable automatic Polar Customer creation on signup
createCustomerOnSignUp: true,
// http://localhost:3000/api/auth/portal
enableCustomerPortal: true,
// Configure checkout
checkout: {
enabled: true,
products: [
{
productId: PRODUCTS.business.id, // ID of Product from Polar Dashboard
slug: PRODUCTS.business.slug, // Custom slug for easy reference in Checkout URL, e.g. /checkout/pro
// http://localhost:3000/api/auth/checkout/business
},
{
productId: PRODUCTS.starter.id,
slug: PRODUCTS.starter.slug,
// http://localhost:3000/api/auth/checkout/starter
},
],
successUrl: "/pages/thanks",
},
// Incoming Webhooks handler will be installed at /polar/webhooks
webhooks: {
// webhooks have to be publicly accessible
// ngrok http http://localhost:3000
secret: process.env.POLAR_WEBHOOK_SECRET as string,
async onPayload(payload) {
switch (payload.type) {
case "subscription.active":
case "subscription.canceled":
case "subscription.updated":
case "subscription.revoked":
case "subscription.created":
case "subscription.uncanceled": {
const authContext = await auth.$context;
const userId = payload.data.customer.externalId;
if (!userId) {
return;
}
if (payload.data.status === "active") {
const productId = payload.data.product.id;
const planType = Object.values(PRODUCTS).find(
(p) => p.id === productId,
)?.slug;
await authContext.internalAdapter.updateUser(userId, {
planType,
});
} else {
// No active subscription, so we need to remove the plan type
await authContext.internalAdapter.updateUser(userId, {
planType: null,
});
use: [
checkout({
products: [
{
productId: PRODUCTS.business.id, // ID of Product from Polar Dashboard
slug: PRODUCTS.business.slug, // Custom slug for easy reference in Checkout URL, e.g. /checkout/pro
},
{
productId: PRODUCTS.starter.id,
slug: PRODUCTS.starter.slug,
},
],
successUrl: "/thanks",
authenticatedUsersOnly: true,
}),
portal(),
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET as string,
async onPayload(payload) {
switch (payload.type) {
case "subscription.active":
case "subscription.canceled":
case "subscription.updated":
case "subscription.revoked":
case "subscription.created":
case "subscription.uncanceled": {
const authContext = await auth.$context;
const userId = payload.data.customer.externalId;
if (!userId) {
return;
}
if (payload.data.status === "active") {
const productId = payload.data.product.id;
const planType = Object.values(PRODUCTS).find(
(p) => p.id === productId,
)?.slug;
await authContext.internalAdapter.updateUser(userId, {
planType,
});
} else {
// No active subscription, so we need to remove the plan type
await authContext.internalAdapter.updateUser(userId, {
planType: null,
});
}
}
}
}
},
},
},
}),
],
}),
],
onAPIError: {
Expand Down
12 changes: 7 additions & 5 deletions fumadocs/components/DocPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import ThemedImage from "@/components/ThemedImage";
import { getMDXComponents } from "@/util/mdx-components";
import { getPageTreePeers } from "fumadocs-core/server";
import { LoaderOutput } from "fumadocs-core/source";
Expand All @@ -16,7 +15,10 @@ export function CardTable({
}) {
return (
<Cards>
{getPageTreePeers(source.pageTree, `/docs/${path}`).map((peer) => (
{getPageTreePeers(
source.pageTree,
`docs/${path.startsWith("/") ? path.slice(1) : path}`,
).map((peer) => (
<Card key={peer.url} title={peer.name} href={peer.url}>
{peer.description}
</Card>
Expand Down Expand Up @@ -63,11 +65,11 @@ export async function DocPage(props: {
<MDXContent
components={getMDXComponents({
// this allows you to link to other pages with relative file paths
// @ts-ignore
a: createRelativeLink(props.source, page),
CardTable: (cardProps) => (
<CardTable source={props.source} path={cardProps.path} />
CardTable: (cardProps: any) => (
<CardTable source={props.source} {...cardProps} />
),
ThemedImage,
})}
/>
</DocsBody>
Expand Down
15 changes: 0 additions & 15 deletions fumadocs/content/docs/advanced/meta.json

This file was deleted.

8 changes: 0 additions & 8 deletions fumadocs/content/docs/collaboration/index.mdx

This file was deleted.

4 changes: 0 additions & 4 deletions fumadocs/content/docs/collaboration/meta.json

This file was deleted.

Loading
Loading