Skip to content
Open
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: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ package-lock.json
results.txt

# Next.js
next-env.d.ts
next-env.d.ts
*.tsbuildinfo
224 changes: 28 additions & 196 deletions apps/website/README.md
Original file line number Diff line number Diff line change
@@ -1,217 +1,49 @@
# Geistdocs
# Streamdown Docs Website

A modern documentation template built with Next.js and [Fumadocs](https://fumadocs.dev). Designed for spinning up Vercel documentation sites quickly and consistently with built-in AI chat, GitHub discussions integration, and a beautiful UI.
The documentation and marketing site for [Streamdown](https://streamdown.ai), built on [Geistdocs](https://github.com/vercel/geistdocs) — Vercel's package-backed docs system powered by Next.js and [Fumadocs](https://fumadocs.dev).

## Features
The shared runtime (layout, docs renderer, search, Ask AI, markdown routes, proxy behavior, and MDX components) is provided by [`@vercel/geistdocs`](https://www.npmjs.com/package/@vercel/geistdocs). This app owns its content, configuration, and thin adapter files.

- 📝 **MDX-powered documentation** - Write docs in MDX with full component support
- 🤖 **AI-powered chat** - Built-in AI assistant that understands your documentation
- 💬 **GitHub Discussions integration** - Allow users to provide feedback directly to GitHub
- 🎨 **Modern UI** - Beautiful, accessible components built with Radix UI
- 🔍 **Advanced search** - Fast, fuzzy search through all documentation
- 🌙 **Dark mode** - Built-in theme switching
- 📱 **Responsive** - Mobile-first design that works everywhere
- ⚡ **Fast** - Built on Next.js 16 with App Router for optimal performance
- 📰 **RSS** - Built-in RSS feed for your documentation
## Structure

## Getting Started
- `content/docs/` - Documentation pages (MDX). Sidebar order is controlled by `content/docs/meta.json`.
- `geistdocs.tsx` - Site configuration (logo, nav, GitHub links, AI prompt and suggestions, agent metadata).
- `lib/geistdocs/` - Source and config adapters wiring the app to `@vercel/geistdocs`.
- `components/geistdocs/` - User-owned adapters for MDX components, the provider, and the docs layout.
- `app/[lang]/(home)/` - The marketing landing page.
- `app/[lang]/playground/` - The interactive Streamdown playground.
- `app/[lang]/docs/` - Docs routes rendered by `createDocsPage` from the package.
- `proxy.ts` - Markdown content negotiation (`.md`/`.mdx` URLs, `Accept: text/markdown`) and i18n handling via `createProxy`.

### Prerequisites
## AI-readable output

- Node.js 18+ and pnpm (recommended) or npm
- A GitHub repository for documentation content
- A GitHub App for discussions integration (optional)
The site serves machine-readable surfaces from package route helpers:

### Installation
- `/llms.txt` - Full documentation corpus as markdown
- `/sitemap.md` - Markdown sitemap with page summaries
- `/agents.md` - Agent-readiness metadata
- Any docs page with a `.md`/`.mdx` suffix - Per-page markdown

1. Create a new repository using this template:

```bash
gh repo create mydocs --template vercel/geistdocs --clone
cd mydocs
```
## Development

2. Install dependencies:
From the repository root:

```bash
pnpm install
pnpm dev --filter website
```

3. Configure environment variables (see [Environment Variables](#environment-variables) below)

4. Run the development server:

```bash
pnpm dev
```

5. Open [http://localhost:3000](http://localhost:3000) in your browser

## Environment Variables
The site consumes the workspace `streamdown` and `@streamdown/*` packages directly, so live demos always render against the local source.

Create a `.env.local` file in the root of your project with the following variables:

### Required Variables
## Updating Geistdocs

```bash
# GitHub Repository Configuration
# The owner of the GitHub repository (organization or username)
NEXT_PUBLIC_GEISTDOCS_OWNER=your-github-username

# The name of your GitHub repository
NEXT_PUBLIC_GEISTDOCS_REPO=your-repo-name

# The name of the GitHub Discussions category for feedback
NEXT_PUBLIC_GEISTDOCS_CATEGORY=Documentation

# GitHub App Credentials (for discussions integration)
# Create a GitHub App at: https://github.com/settings/apps/new
GITHUB_APP_ID=your-app-id
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"

# AI Gateway API Key (for AI chat functionality)
# This is typically an OpenAI API key or compatible AI gateway
AI_GATEWAY_API_KEY=your-api-key

# Production URL (set automatically on Vercel)
NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL=yourdomain.com
npx @vercel/geistdocs update
```

### Setting Up GitHub App

To enable the feedback feature that creates GitHub Discussions:

1. Go to [GitHub Apps settings](https://github.com/settings/apps/new)
2. Create a new GitHub App with the following permissions:
- Repository permissions:
- Discussions: Read and write
- Subscribe to events: Discussions
3. Generate a private key and save it
4. Install the app on your documentation repository
5. Add the App ID and private key to your `.env.local`

### Setting Up AI Chat

The AI chat feature uses the Vercel AI SDK with OpenAI:

1. Get an API key from [OpenAI](https://platform.openai.com/api-keys)
2. Add it as `AI_GATEWAY_API_KEY` in your `.env.local`
3. The chat will automatically search and answer questions about your documentation

## Project Structure

```
geistdocs/
├── app/
│ ├── (home)/ # Landing page and marketing content
│ ├── docs/ # Documentation pages
│ ├── api/
│ │ ├── chat/ # AI chat API endpoint
│ │ └── search/ # Search API endpoint
│ └── actions/ # Server actions (e.g., GitHub discussions)
├── components/
│ ├── geistdocs/ # Custom Geistdocs components
│ └── ui/ # Reusable UI components
├── content/ # MDX documentation content
├── lib/
│ ├── source.ts # Content source adapter
│ └── layout.shared.tsx # Shared layout configuration
└── source.config.ts # Fumadocs MDX configuration
```

## Writing Documentation

Documentation is written in MDX format in the `content/` directory. Each file can include:

- **Frontmatter** - Metadata like title, description, and more
- **MDX components** - Use React components directly in your markdown
- **Code blocks** - Syntax-highlighted code with Shiki

Example:

```mdx
---
title: Getting Started
description: Learn how to get started with Geistdocs
---

# Getting Started

Welcome to the documentation!

<Callout type="info">
This is a custom component you can use in your docs.
</Callout>
```

## Customization

### Styling

The project uses Tailwind CSS 4. Customize the theme in `tailwind.config.ts` and global styles in `app/globals.css`.

### Components

Add custom components to `components/geistdocs/` and import them in your MDX files.

### Layout

Modify the shared layout options in `lib/layout.shared.tsx` to customize the sidebar, navigation, and more.

## Development

```bash
# Start development server
pnpm dev

# Build for production
pnpm build

# Start production server
pnpm start

# Run linter
pnpm lint

# Format code
pnpm format
```

## Code Quality

This project uses [Ultracite](https://github.com/haydenbleasel/ultracite), a zero-config Biome preset for TypeScript/React projects. All code is automatically formatted and linted with strict rules for:

- Type safety
- Accessibility
- Performance
- React best practices

Run `pnpm lint` to check for issues and `pnpm format` to auto-fix formatting.

## Deployment

### Vercel (Recommended)

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/geistdocs)

1. Click the "Deploy" button above
2. Configure your environment variables in the Vercel dashboard
3. Deploy!

### Other Platforms

This is a standard Next.js application and can be deployed to any platform that supports Node.js:

1. Build the application: `pnpm build`
2. Start the server: `pnpm start`
3. Ensure all environment variables are set

## Learn More

- [Next.js Documentation](https://nextjs.org/docs) - Learn about Next.js features and API
- [Fumadocs](https://fumadocs.dev) - Learn about Fumadocs
- [Ultracite](https://github.com/haydenbleasel/ultracite) - Learn about code quality standards
This bumps the `@vercel/geistdocs` dependency. Local adapter files are user-owned and never overwritten.

## License
## Environment variables

MIT
- `NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL` - Production URL used for absolute links (set automatically on Vercel)
- `AI_GATEWAY_API_KEY` - Enables the Ask AI assistant (set automatically on Vercel)
18 changes: 11 additions & 7 deletions apps/website/app/[lang]/(home)/components/installer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,37 @@ import {
InputGroupText,
} from "@/components/ui/input-group";

const CODE = "npm i streamdown";
const TIMEOUT = 2000;
const COPY_TIMEOUT = 2000;

export const Installer = () => {
interface InstallerProps {
className?: string;
command: string;
}

export const Installer = ({ command, className = "w-48" }: InstallerProps) => {
const [copied, setCopied] = useState(false);

const handleCopy = () => {
navigator.clipboard.writeText(CODE);
navigator.clipboard.writeText(command);
toast.success("Copied to clipboard");
setCopied(true);

track("Copied installer command");
setTimeout(() => {
setCopied(false);
}, TIMEOUT);
}, COPY_TIMEOUT);
};

const Icon = copied ? CheckIcon : CopyIcon;

return (
<InputGroup className="font-mono shadow-none">
<InputGroup className="h-10 bg-background font-mono shadow-none">
<InputGroupAddon>
<InputGroupText className="font-normal text-muted-foreground">
$
</InputGroupText>
</InputGroupAddon>
<InputGroupInput readOnly value={CODE} />
<InputGroupInput className={className} readOnly value={command} />
<InputGroupAddon align="inline-end">
<InputGroupButton
aria-label="Copy"
Expand Down
9 changes: 5 additions & 4 deletions apps/website/app/[lang]/(home)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { HomeLayout } from "@/components/geistdocs/home-layout";
import { GeistdocsHomeLayout } from "@vercel/geistdocs/home-layout";
import { config } from "@/lib/geistdocs/config";
import { source } from "@/lib/geistdocs/source";

const Layout = async ({ children, params }: LayoutProps<"/[lang]">) => {
const { lang } = await params;

return (
<HomeLayout tree={source.pageTree[lang]}>
<div className="bg-sidebar pt-0 pb-32 dark:bg-background">{children}</div>
</HomeLayout>
<GeistdocsHomeLayout config={config} tree={source.pageTree[lang]}>
<div className="pt-0 pb-32">{children}</div>
</GeistdocsHomeLayout>
);
};

Expand Down
2 changes: 1 addition & 1 deletion apps/website/app/[lang]/(home)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import DynamicLink from "fumadocs-core/dynamic-link";
import type { Metadata } from "next";
import { Installer } from "@/components/geistdocs/installer";
import { Button } from "@/components/ui/button";
import { CenteredSection } from "./components/centered-section";
import { CTA } from "./components/cta";
import { Demo } from "./components/demo";
import { Hero } from "./components/hero";
import { Installer } from "./components/installer";
import { Logos } from "./components/logos";
import { OneTwoSection } from "./components/one-two-section";
import { Templates } from "./components/templates";
Expand Down
8 changes: 8 additions & 0 deletions apps/website/app/[lang]/.well-known/mcp.json/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createMcpManifestRoute } from "@vercel/geistdocs/routes/mcp";
import { config } from "@/lib/geistdocs/config";

export const { GET, generateStaticParams, revalidate } = createMcpManifestRoute(
{
config,
}
);
6 changes: 6 additions & 0 deletions apps/website/app/[lang]/agents.md/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createAgentsRoute } from "@vercel/geistdocs/routes/agents";
import { config } from "@/lib/geistdocs/config";

export const { GET, generateStaticParams, revalidate } = createAgentsRoute({
config,
});
Loading
Loading