Skip to content

Commit a66a5a3

Browse files
Update documentation style (#235)
* Copy Geistdocs files * Fix wiring * More wiring * Upgrade Fumadocs * Reorganize custom components * Restore ai-sdk nav * Update files * Update client-navbar.tsx * Update elements-installer.tsx * Disable Ask AI * Update source.ts * Update preview.tsx * Build fixes * Update elements-demo.tsx * Update next-env.d.ts * Update client-navbar.tsx * Update mdx-components * Update client-navbar.tsx * Update client-navbar.tsx * Update docs-page.tsx * Update global.css * Update code-block.tsx * Update code-block.tsx * AI SDK website patches * Update banner.tsx * Update toc.tsx * Responsive fixes * Update banner.tsx * Update og image * Fix banner on dark mode, add theme toggle * Update route.tsx
1 parent a1b1929 commit a66a5a3

66 files changed

Lines changed: 3312 additions & 553 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 11 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,21 @@
11
import {
2-
OpenIn,
3-
OpenInChatGPT,
4-
OpenInClaude,
5-
OpenInContent,
6-
OpenInCursor,
7-
OpenInScira,
8-
OpenInSeparator,
9-
OpenInT3,
10-
OpenInTrigger,
11-
OpenInv0,
12-
} from "@repo/elements/open-in-chat";
13-
import { Button } from "@repo/shadcn-ui/components/ui/button";
14-
import { createRelativeLink } from "fumadocs-ui/mdx";
15-
import {
16-
DocsBody,
17-
DocsDescription,
182
DocsPage,
19-
DocsTitle,
20-
} from "fumadocs-ui/page";
21-
import { ChevronDownIcon } from "lucide-react";
22-
import type { Metadata } from "next";
23-
import { notFound } from "next/navigation";
24-
import { CopyMarkdown } from "@/components/copy-markdown";
25-
import { getLLMText } from "@/lib/get-llm-text";
26-
import { source } from "@/lib/source";
27-
import { getMDXComponents } from "@/mdx-components";
3+
generatePageMetadata,
4+
generateStaticPageParams,
5+
} from "@/components/geistdocs/docs-page";
286

29-
export default async function Page(props: PageProps<"/[[...slug]]">) {
7+
const Page = async (props: PageProps<"/[[...slug]]">) => {
308
const params = await props.params;
31-
const page = source.getPage(params.slug);
32-
33-
if (!page) {
34-
notFound();
35-
}
369

37-
const MDXContent = page.data.body;
38-
const parsedUrl = page.url === "/" ? "/index" : page.url;
39-
const markdownUrl = `/elements${parsedUrl}.mdx`;
40-
const protocol = process.env.NODE_ENV === "production" ? "https" : "http";
41-
const fullMarkdownUrl = new URL(
42-
markdownUrl,
43-
`${protocol}://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`
44-
);
45-
const query = `Read ${fullMarkdownUrl}, I want to ask questions about it.`;
46-
const text = await getLLMText(page);
47-
48-
return (
49-
<DocsPage
50-
container={{
51-
className: "max-w-[75rem]",
52-
}}
53-
full={page.data.full}
54-
tableOfContent={{
55-
style: "clerk",
56-
}}
57-
toc={page.data.toc}
58-
>
59-
<DocsTitle>{page.data.title}</DocsTitle>
60-
<DocsDescription>{page.data.description}</DocsDescription>
61-
<div className="-mt-8 mb-8 flex flex-row items-center gap-2">
62-
<CopyMarkdown text={text} />
63-
<OpenIn query={query}>
64-
<OpenInTrigger>
65-
<Button size="sm" type="button" variant="outline">
66-
Open in
67-
<ChevronDownIcon className="size-4" />
68-
</Button>
69-
</OpenInTrigger>
70-
<OpenInContent
71-
align="end"
72-
alignOffset={-36}
73-
collisionPadding={8}
74-
side="bottom"
75-
sideOffset={8}
76-
>
77-
<OpenInv0 />
78-
<OpenInSeparator />
79-
<OpenInChatGPT />
80-
<OpenInClaude />
81-
<OpenInT3 />
82-
<OpenInScira />
83-
<OpenInCursor />
84-
</OpenInContent>
85-
</OpenIn>
86-
</div>
87-
<DocsBody>
88-
<MDXContent
89-
components={getMDXComponents({
90-
// this allows you to link to other pages with relative file paths
91-
a: createRelativeLink(source, page),
92-
})}
93-
/>
94-
</DocsBody>
95-
</DocsPage>
96-
);
97-
}
10+
return <DocsPage slug={params.slug} />;
11+
};
9812

99-
export const generateStaticParams = () => source.generateParams();
13+
export const generateStaticParams = generateStaticPageParams;
10014

101-
export const generateMetadata = async (
102-
props: PageProps<"/[[...slug]]">
103-
): Promise<Metadata> => {
15+
export const generateMetadata = async (props: PageProps<"/[[...slug]]">) => {
10416
const params = await props.params;
105-
const page = source.getPage(params.slug);
10617

107-
if (!page) {
108-
notFound();
109-
}
110-
111-
const title = `${page.data.title} | ▲ AI Elements`;
112-
const description = page.data.description;
113-
const protocol = process.env.NODE_ENV === "production" ? "https" : "http";
114-
const baseUrl = `${protocol}://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`;
115-
const image = new URL(`/og?slug=${params.slug?.join("/") ?? ""}`, baseUrl);
116-
117-
return {
118-
title,
119-
description,
120-
openGraph: {
121-
title,
122-
description,
123-
type: "website",
124-
images: [
125-
{
126-
url: image,
127-
width: 1200,
128-
height: 630,
129-
},
130-
],
131-
},
132-
twitter: {
133-
title,
134-
description,
135-
images: [
136-
{
137-
url: image,
138-
width: 1200,
139-
height: 630,
140-
},
141-
],
142-
card: "summary_large_image",
143-
creator: "@vercel",
144-
},
145-
};
18+
return generatePageMetadata(params.slug);
14619
};
20+
21+
export default Page;

apps/docs/app/(docs)/layout.tsx

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,7 @@
1-
import { DocsLayout } from "fumadocs-ui/layouts/docs";
2-
import { AiSdkNav } from "@/components/navbar";
3-
import { source } from "@/lib/source";
1+
import { DocsLayout } from "@/components/geistdocs/docs-layout";
42

53
const Layout = ({ children }: LayoutProps<"/">) => (
6-
<DocsLayout
7-
links={[]}
8-
nav={{
9-
component: <AiSdkNav />,
10-
}}
11-
searchToggle={{
12-
enabled: false,
13-
}}
14-
sidebar={{
15-
collapsible: false,
16-
tabs: [],
17-
className: "bg-background! transition-none! border-none!",
18-
}}
19-
tree={source.pageTree}
20-
>
21-
{children}
22-
</DocsLayout>
4+
<DocsLayout>{children}</DocsLayout>
235
);
246

257
export default Layout;

apps/docs/app/actions/discuss.ts

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"use server";
2+
3+
import { App, type Octokit } from "octokit";
4+
import type { ActionResponse, Feedback } from "@/components/geistdocs/feedback";
5+
6+
const getOctokit = async (): Promise<Octokit> => {
7+
const repo = process.env.NEXT_PUBLIC_GEISTDOCS_REPO;
8+
const owner = process.env.NEXT_PUBLIC_GEISTDOCS_OWNER;
9+
const category = process.env.NEXT_PUBLIC_GEISTDOCS_CATEGORY;
10+
const appId = process.env.GITHUB_APP_ID;
11+
const privateKey = process.env.GITHUB_APP_PRIVATE_KEY?.replace(/\\n/g, "\n");
12+
13+
if (!(repo && owner && category && appId && privateKey)) {
14+
throw new Error("Missing environment variables");
15+
}
16+
17+
const app = new App({ appId, privateKey });
18+
19+
const { data } = await app.octokit.request(
20+
"GET /repos/{owner}/{repo}/installation",
21+
{
22+
owner,
23+
repo,
24+
headers: {
25+
"X-GitHub-Api-Version": "2022-11-28",
26+
},
27+
}
28+
);
29+
30+
return await app.getInstallationOctokit(data.id);
31+
};
32+
33+
type RepositoryInfo = {
34+
id: string;
35+
discussionCategories: {
36+
nodes: {
37+
id: string;
38+
name: string;
39+
}[];
40+
};
41+
};
42+
43+
const getFeedbackDestination = async () => {
44+
const octokit = await getOctokit();
45+
const owner = process.env.NEXT_PUBLIC_GEISTDOCS_OWNER;
46+
const repo = process.env.NEXT_PUBLIC_GEISTDOCS_REPO;
47+
48+
if (!(owner && repo)) {
49+
throw new Error("Missing environment variables");
50+
}
51+
52+
const {
53+
repository,
54+
}: {
55+
repository: RepositoryInfo;
56+
} = await octokit.graphql(`
57+
query {
58+
repository(owner: "${owner}", name: "${repo}") {
59+
id
60+
discussionCategories(first: 25) {
61+
nodes { id name }
62+
}
63+
}
64+
}
65+
`);
66+
67+
return repository;
68+
};
69+
70+
export const discuss = async (
71+
url: string,
72+
feedback: Feedback
73+
): Promise<ActionResponse> => {
74+
const owner = process.env.NEXT_PUBLIC_GEISTDOCS_OWNER;
75+
const repo = process.env.NEXT_PUBLIC_GEISTDOCS_REPO;
76+
const docsCategory = process.env.NEXT_PUBLIC_GEISTDOCS_CATEGORY;
77+
78+
if (!(owner && repo && docsCategory)) {
79+
throw new Error("Missing environment variables");
80+
}
81+
82+
const octokit = await getOctokit();
83+
const destination = await getFeedbackDestination();
84+
const category = destination.discussionCategories.nodes.find(
85+
({ name }) => name === docsCategory
86+
);
87+
88+
if (!category) {
89+
throw new Error(
90+
`Please create a "${docsCategory}" category in GitHub Discussion`
91+
);
92+
}
93+
94+
const title = `Feedback for ${url}`;
95+
const emoji = feedback.emotion === "good" ? "👍" : "👎";
96+
const body = `${emoji} ${feedback.message}\n\n> Forwarded from user feedback.`;
97+
98+
let {
99+
search: {
100+
nodes: [discussion],
101+
},
102+
}: {
103+
search: {
104+
nodes: { id: string; url: string }[];
105+
};
106+
} = await octokit.graphql(`
107+
query {
108+
search(type: DISCUSSION, query: ${JSON.stringify(`${title} in:title repo:${owner}/${repo} author:@me`)}, first: 1) {
109+
nodes {
110+
... on Discussion { id, url }
111+
}
112+
}
113+
}`);
114+
115+
if (discussion) {
116+
await octokit.graphql(`
117+
mutation {
118+
addDiscussionComment(input: { body: ${JSON.stringify(body)}, discussionId: "${discussion.id}" }) {
119+
comment { id }
120+
}
121+
}`);
122+
} else {
123+
const result: {
124+
discussion: { id: string; url: string };
125+
} = await octokit.graphql(`
126+
mutation {
127+
createDiscussion(input: { repositoryId: "${destination.id}", categoryId: "${category.id}", body: ${JSON.stringify(body)}, title: ${JSON.stringify(title)} }) {
128+
discussion { id, url }
129+
}
130+
}`);
131+
132+
discussion = result.discussion;
133+
}
134+
135+
return {
136+
githubUrl: discussion.url,
137+
};
138+
};

0 commit comments

Comments
 (0)