Compare commits

..

1 Commits

Author SHA1 Message Date
javayhu
0d0c93dcfb feat: support ui.pub themes
https://ui.pub/x/theme-gen
2025-04-01 11:56:28 +08:00
843 changed files with 18278 additions and 55630 deletions

View File

@ -5,10 +5,6 @@
"args": [
"~/.console-ninja/mcp/"
]
},
"context7": {
"command": "npx",
"args": ["-y", "@upstash/context7-mcp@latest"]
}
}
}

View File

@ -1,6 +1,6 @@
---
description: Best practices for using Vercel AI SDK
globs: *.tsx,*.ts
globs: **/*.{ts,tsx}
alwaysApply: false
---

View File

@ -1,43 +0,0 @@
---
description:
globs: *.tsx,*.ts
alwaysApply: false
---
# Database and State Management Guide
## Database (Drizzle ORM)
- Schema definitions in `src/db/schema.ts`
- Migrations in `src/db/migrations`
- Use `db:generate` to create new migration files based on schema changes
- Use `db:migrate` to apply pending migrations to the database
- Use `db:push` to sync schema changes directly to the database (development only)
- Use `db:studio` to view and manage database data through the Drizzle Studio UI
- Follow naming conventions for tables and columns
- Use proper data types and constraints
- Implement proper indexes
- Handle relationships properly
- Use transactions when needed
## State Management (Zustand)
- Store definitions in `src/stores/`
- Keep stores modular and focused
- Use TypeScript for store types
- Implement proper state updates
- Handle async operations properly
- Use selectors for derived state
- Implement proper error handling
- Use middleware when needed
- Keep store logic pure
- Document complex state logic
## Data Flow
1. Server-side data fetching in server components
2. Client-side state in Zustand stores
3. Form state in React Hook Form
4. API calls through server actions
5. Database operations through Drizzle
6. File storage through AWS S3
7. Proper error handling at each layer
8. Type safety throughout
9. Proper validation with Zod
10. Proper caching strategies

View File

@ -1,10 +1,10 @@
---
description: Best practices for date and time manipulation with date-fns
globs: *.ts,*.tsx
globs: **/*.{ts,tsx,js,jsx}
alwaysApply: false
---
- Use the `format` function for consistent date formatting across your application.
- Implement proper timezone handling using the `utcToZonedTime` function.
- Utilize the `intervalToDuration` function for calculating time differences.
- Leverage the `isWithinInterval` function for date range checks.
- Leverage the `isWithinInterval` function for date range checks.

View File

@ -1,39 +0,0 @@
---
description:
globs: *.tsx,*.ts
alwaysApply: false
---
# Development Workflow Guide
## Available Scripts
- `pnpm dev`: Start development server with content collections
- `pnpm build`: Build the application and content collections
- `pnpm start`: Start production server
- `pnpm lint`: Run Biome linter
- `pnpm format`: Format code with Biome
- `pnpm db:generate`: Generate new migration files based on schema changes
- `pnpm db:migrate`: Apply pending migrations to the database
- `pnpm db:push`: Sync schema changes directly to the database (development only)
- `pnpm db:studio`: Open Drizzle Studio for database inspection and management
- `pnpm email`: Start email template development server
## Development Process
1. Use TypeScript for all new code
2. Follow Biome formatting rules
3. Write server actions in `src/actions/`
4. Use Zustand for client-side state
5. Implement database changes through Drizzle migrations
6. Use Radix UI components for consistent UI
7. Follow the established directory structure
8. Write tests for new features
9. Update content collections when adding new content
10. Use environment variables from `env.example`
## Code Style
- Use functional components with hooks
- Implement proper error handling
- Follow TypeScript best practices
- Use proper type definitions
- Document complex logic
- Keep components small and focused
- Use proper naming conventions

View File

@ -1,7 +1,6 @@
---
description: Best practices for using Drizzle ORM with database
globs: *.tsx,*.ts
alwaysApply: false
globs: **/*.{ts}
---
- Use Drizzle's type-safe query builder for better code completion and safety.

View File

@ -1,6 +1,6 @@
---
description: Best practices for Next.js applications and routing
globs: *.tsx,*.ts
globs: **/*.{ts,tsx}
alwaysApply: false
---

View File

@ -1,37 +0,0 @@
---
description:
globs: **/*.{ts,tsx}
alwaysApply: false
---
# Project Structure Guide
## Core Directories
- `src/app/`: Next.js app router pages and layouts
- `src/components/`: Reusable React components
- `src/lib/`: Utility functions and shared code
- `src/db/`: Database schema and migrations using Drizzle ORM
- `src/stores/`: Zustand state management
- `src/actions/`: Server actions and API routes
- `src/hooks/`: Custom React hooks
- `src/types/`: TypeScript type definitions
- `src/i18n/`: Internationalization setup
- `src/mail/`: Email templates and mail functionality
- `src/payment/`: Payment integration
- `src/analytics/`: Analytics and tracking
- `src/storage/`: File storage integration
- `src/notification/`: Sending Notifications
## Configuration Files
- `next.config.ts`: Next.js configuration
- `drizzle.config.ts`: Database configuration
- `biome.json`: Code formatting and linting rules
- `tsconfig.json`: TypeScript configuration
- `components.json`: UI components configuration
## Content Management
- `content/`: MDX content files
- `source.config.ts`: Fumadocs source configuration
## Environment
- `env.example`: Environment variables template
- `global.d.ts`: Global TypeScript declarations

View File

@ -1,6 +1,6 @@
---
description: Best practices for using Radix UI components
globs: *.tsx,*.ts
globs: **/*.{ts,tsx}
alwaysApply: false
---

View File

@ -1,6 +1,6 @@
---
description: Best practices for React component development
globs: *.tsx,*.ts
globs: **/*.{ts,tsx,js,jsx}
alwaysApply: false
---

View File

@ -1,10 +1,10 @@
---
description: Best practices for form handling with React Hook Form
globs: *.tsx,*.ts
globs: **/*.{ts,tsx}
alwaysApply: false
---
- Use the `useForm` hook for efficient form state management.
- Implement validation using Zod with `@hookform/resolvers` for type-safe form validation.
- Utilize the `Controller` component for integrating with custom inputs.
- Leverage the `useFormContext` hook for sharing form state across components.
- Leverage the `useFormContext` hook for sharing form state across components.

View File

@ -1,6 +1,6 @@
---
description: Best practices for integrating Stripe payments
globs: *.tsx,*.ts
globs: **/*.{ts,tsx}
alwaysApply: false
---

View File

@ -1,6 +1,6 @@
---
description: Best practices for styling with Tailwind CSS
globs: *.tsx,*.ts
globs: **/*.{ts,tsx,css}
alwaysApply: false
---

View File

@ -1,6 +1,6 @@
---
description: TypeScript coding standards and type safety guidelines
globs: *.tsx,*.ts
globs: **/*.{ts,tsx}
alwaysApply: false
---
@ -11,4 +11,4 @@ alwaysApply: false
- Use strict null checks to prevent null and undefined errors
- Implement proper type inference using generics for reusable components.
- Utilize type guards and assertions for runtime type checking.
- Use `pnpm` as default package manager if run Command in Terminal.
- Use `pnpm` as default package manager if run Command in Terminal.

View File

@ -1,54 +0,0 @@
---
description:
globs: **/*.{ts,tsx}
alwaysApply: false
---
# UI and Components Guide
## Component Structure
- Components in `src/components/`
- Follow atomic design principles
- Use Radix UI primitives
- Implement proper accessibility
- Use Tailwind CSS for styling
- Follow consistent naming
- Keep components focused
- Implement proper error states
- Handle loading states
- Use proper TypeScript types
## UI Libraries
- Radix UI for primitives
- Tailwind CSS for styling
- Framer Motion for animations
- React Hook Form for forms
- Zod for validation
- Lucide React for icons
- Tabler Icons for additional icons
- Sonner for toasts
- Vaul for drawers
- Embla Carousel for carousels
## Styling Guidelines
- Use Tailwind CSS classes
- Follow design system tokens
- Implement dark mode support
- Use proper spacing scale
- Follow color palette
- Implement responsive design
- Use proper typography
- Handle hover/focus states
- Implement proper transitions
- Use proper z-index scale
## Accessibility
- Use semantic HTML
- Implement proper ARIA labels
- Handle keyboard navigation
- Support screen readers
- Use proper color contrast
- Implement focus management
- Handle dynamic content
- Support reduced motion
- Test with assistive tools
- Follow WCAG guidelines

View File

@ -1,7 +1,6 @@
---
description: Best practices for schema validation with Zod
globs: *.tsx,*.ts
alwaysApply: false
globs: **/*.{ts,tsx}
---
- Define clear and reusable schemas for data validation

View File

@ -1,10 +1,10 @@
---
description: Best practices for state management with Zustand
globs: *.tsx,*.ts
globs: **/*.{ts,tsx}
alwaysApply: false
---
- Use the `create` function to define your store for simplicity and performance.
- Implement middleware like `persist` for persisting state across sessions.
- Utilize the `useStore` hook for accessing store state in components.
- Leverage the `immer` middleware for easier state updates with mutable syntax.
- Leverage the `immer` middleware for easier state updates with mutable syntax.

46
.cursorrules Normal file
View File

@ -0,0 +1,46 @@
You are an expert in TypeScript, Node.js, Next.js with the app router, React, Shadcn/ui, Magic UI, Tailwind, Better Auth, Drizzle ORM, Content Collections, and Vercel AI SDK.
General Principles
- Write clean, concise, and well-commented TypeScript code
- Favor functional and declarative programming patterns over object-oriented approaches
- Prioritize code reuse and modularization over duplication
Naming and Conventions
- Use PascalCase for class names and type definitions
- Utilize camelCase for variables, functions, and methods
- Employ kebab-case for file and directory names
- Reserve UPPERCASE for environment variables and constants
- Avoid magic numbers by defining constants with meaningful names
- Start each function name with a verb to indicate its purpose
TypeScript Usage
- Leverage TypeScript for all code
- Prefer types over interfaces
- Favor functional components over class components
Code Organization
- Structure files logically, grouping related components, helpers, types, and static content
- Prefer named exports for components over default exports
- Favor small, single-purpose components over large, monolithic ones
- Separate concerns between presentational and container components
UI and Styling
- Utilize Shadcn/ui, Radix, and Tailwind for building consistent and accessible UI components
- Implement responsive design using Tailwind CSS with a mobile-first approach
- Use Magic UI for advanced components and animations
- Use Lucide for icons
Data Management
- Interact with the database using Drizzle ORM
- Leverage Drizzle's generated types
Next.js and React
- Minimize the use of `use client`, `useEffect`, and `setState`
- Favor React Server Components (RSC) whenever possible
- Wrap client-side components in `Suspense` with a fallback
- Implement dynamic loading for non-critical components
- Use server actions for mutations instead of route handlers
Error Handling and Logging
- Implement robust error handling and logging mechanisms
- Provide clear and user-friendly error messages to the end-users

View File

@ -1,16 +0,0 @@
.cursor
.claude
.conductor
.kiro
.github
.next
.open-next
.source
.vscode
.git
.wrangler
.dockerignore
node_modules
**/node_modules
Dockerfile
LICENSE

41
.gitattributes vendored
View File

@ -1,41 +0,0 @@
# Set default behavior to automatically normalize line endings
* text=auto
# Force LF line endings for text files
*.js text eol=lf
*.jsx text eol=lf
*.ts text eol=lf
*.tsx text eol=lf
*.json text eol=lf
*.md text eol=lf
*.mdx text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.css text eol=lf
*.scss text eol=lf
*.html text eol=lf
*.xml text eol=lf
*.txt text eol=lf
*.sh text eol=lf
# Ensure these files are always treated as text and get LF line endings
.gitignore text eol=lf
.gitattributes text eol=lf
.editorconfig text eol=lf
*.config.js text eol=lf
*.config.ts text eol=lf
# Binary files should be left untouched
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.svg binary
*.woff binary
*.woff2 binary
*.ttf binary
*.eot binary
*.pdf binary
*.zip binary
*.tar.gz binary

View File

@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: javayhu
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone16]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -1,8 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: MkSaaS Community Support
url: https://github.com/MkSaaSHQ/mksaas-template/discussions
about: Please ask and answer questions here.
- name: MkSaaS Documentation
url: https://mksaas.com/docs
about: Please check out the documentation here.

View File

@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[feat]"
labels: enhancement
assignees: javayhu
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

25
.gitignore vendored
View File

@ -30,38 +30,15 @@ yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
certificates
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# claude code
.claude
# conductor
.conductor
# kiro
.kiro
# typescript
*.tsbuildinfo
next-env.d.ts
# content collections
.content-collections
# fumadocs
.source
# OpenNext build output
.open-next
# wrangler files
.wrangler
.dev.vars
.dev.vars*
!.dev.vars.example
.content-collections

View File

@ -1,10 +0,0 @@
{
"recommendations": [
"biomejs.biome",
"bradlc.vscode-tailwindcss",
"Lokalise.i18n-ally",
"unifiedjs.vscode-mdx",
"eamodio.gitlens",
"editorconfig.editorconfig"
]
}

40
.vscode/settings.json vendored
View File

@ -1,35 +1,7 @@
{
"i18n-ally.localesPaths": [
"messages",
"src/i18n"
],
"i18n-ally.keystyle": "nested",
"editor.defaultFormatter": "biomejs.biome",
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
"search.exclude": {
"**/node_modules": true,
".next": true,
".source": true,
".wrangler": true,
".open-next": true,
".vscode": true,
".cursor": true,
".claude": true,
".conductor": true,
".kiro": true,
".github": true
}
}
"i18n-ally.localesPaths": [
"messages",
"src/i18n"
],
"i18n-ally.keystyle": "nested"
}

109
CLAUDE.md
View File

@ -1,109 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Commands
### Core Development
- `pnpm dev` - Start development server with content collections
- `pnpm build` - Build the application and content collections
- `pnpm start` - Start production server
- `pnpm lint` - Run Biome linter (use for code quality checks)
- `pnpm format` - Format code with Biome
### Database Operations (Drizzle ORM)
- `pnpm db:generate` - Generate new migration files based on schema changes
- `pnpm db:migrate` - Apply pending migrations to the database
- `pnpm db:push` - Sync schema changes directly to the database (development only)
- `pnpm db:studio` - Open Drizzle Studio for database inspection and management
### Content and Email
- `pnpm content` - Process MDX content collections
- `pnpm email` - Start email template development server on port 3333
## Project Architecture
This is a Next.js 15 full-stack SaaS application with the following key architectural components:
### Core Stack
- **Framework**: Next.js 15 with App Router
- **Database**: PostgreSQL with Drizzle ORM
- **Authentication**: Better Auth with social providers (Google, GitHub)
- **Payments**: Stripe integration with subscription and one-time payments
- **UI**: Radix UI components with TailwindCSS
- **State Management**: Zustand for client-side state
- **Internationalization**: next-intl with English and Chinese locales
- **Content**: Fumadocs for documentation and MDX for content
- **Code Quality**: Biome for formatting and linting
### Key Directory Structure
- `src/app/` - Next.js app router with internationalized routing
- `src/components/` - Reusable React components organized by feature
- `src/lib/` - Utility functions and shared code
- `src/db/` - Database schema and migrations
- `src/actions/` - Server actions for API operations
- `src/stores/` - Zustand state management
- `src/hooks/` - Custom React hooks
- `src/config/` - Application configuration files
- `src/i18n/` - Internationalization setup
- `src/mail/` - Email templates and mail functionality
- `src/payment/` - Stripe payment integration
- `src/credits/` - Credit system implementation
- `content/` - MDX content files for docs and blog
- `messages/` - Translation files (en.json, zh.json) for internationalization
### Authentication & User Management
- Uses Better Auth with PostgreSQL adapter
- Supports email/password and social login (Google, GitHub)
- Includes user management, email verification, and password reset
- Admin plugin for user management and banning
- Automatic newsletter subscription on user creation
### Payment System
- Stripe integration for subscriptions and one-time payments
- Three pricing tiers: Free, Pro (monthly/yearly), and Lifetime
- Credit system with packages for pay-per-use features
- Customer portal for subscription management
### Feature Modules
- **Blog**: MDX-based blog with pagination and categories
- **Docs**: Fumadocs-powered documentation
- **AI Features**: Image generation with multiple providers (OpenAI, Replicate, etc.)
- **Newsletter**: Email subscription system
- **Analytics**: Multiple analytics providers support
- **Storage**: S3 integration for file uploads
### Development Workflow
1. Use TypeScript for all new code
2. Follow Biome formatting rules (single quotes, trailing commas)
3. Write server actions in `src/actions/`
4. Use Zustand for client-side state management
5. Implement database changes through Drizzle migrations
6. Use Radix UI components for consistent UI
7. Follow the established directory structure
8. Use proper error handling with error.tsx and not-found.tsx
9. Leverage Next.js 15 features like Server Actions
10. Use `next-safe-action` for secure form submissions
### Configuration
- Main config in `src/config/website.tsx`
- Environment variables template in `env.example`
- Database config in `drizzle.config.ts`
- Biome config in `biome.json` with specific ignore patterns
- TypeScript config with path aliases (@/* for src/*)
### Testing and Quality
- Use Biome for linting and formatting
- TypeScript for type safety
- Environment variables for configuration
- Proper error boundaries and not-found pages
- Zod for runtime validation
## Important Notes
- The project uses pnpm as the package manager
- Database schema is in `src/db/schema.ts` with auth, payment, and credit tables
- Email templates are in `src/mail/templates/`
- The app supports both light and dark themes
- Content is managed through MDX files in the `content/` directory
- The project includes comprehensive internationalization support

View File

@ -1,62 +0,0 @@
# syntax=docker/dockerfile:1
FROM node:20-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies
COPY package.json pnpm-lock.yaml* ./
# Copy config files needed for fumadocs-mdx postinstall
COPY source.config.ts ./
COPY content ./content
RUN npm install -g pnpm && pnpm i --frozen-lockfile
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN npm install -g pnpm \
&& DOCKER_BUILD=true pnpm build
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD ["node", "server.js"]

1
ISSUES.md Normal file
View File

@ -0,0 +1 @@
1. When navigate to docs and navigate back to homepage, the page layout may shift when click the action buttons on the navbar

41
LICENSE
View File

@ -1,41 +0,0 @@
MkSaaS License
Personal, Team, or Organization License
MkSaaS grants you an ongoing, non-exclusive license to use the software components and templates included in the SaaS Starter Kit.
The license permits usage by a single individual, team, or organization (the Licensee) and does not transfer to additional individuals, teams, or organizations without explicit permission.
You can:
- Use MkSaaS to create unlimited end products.
- Modify MkSaaS components to create derivative works, subject to this license.
- Use MkSaaS to create projects for unlimited clients.
- Use MkSaaS to create end products sold to end users.
- Use MkSaaS to build and run SaaS applications where end users interact with your end product.
You cannot:
- Redistribute MkSaaSs components or templates, even if modified, separately from an end product.
- Share your access to MkSaaS with anyone outside your team or organization without explicit permission.
- Use MkSaaS to create products that compete with MkSaaS or provide a SaaS starter kit in conflict with MkSaaSs business.
Example Usage
Examples of permitted usage:
- Creating personal, team, or organizational websites or applications.
- Building commercial SaaS applications or web apps for clients.
Examples of prohibited usage:
- Creating a repository of MkSaaS (components) and sharing or selling it.
- Making a derivative product (e.g. starter kit) and offering it for sale or free.
Enforcement and Liability
MkSaaS reserves the right to revoke licenses in case of a material breach of this agreement. The liability of MkSaaS is limited to the refund of the license fee. This agreement is governed by the laws of Switzerland.
Questions?
Unsure if your use case is covered by the license? Email us at support@MkSaaS.com with your questions.

View File

@ -1,46 +1,36 @@
# MkSaaS
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
Make AI SaaS in a weekend.
## Getting Started
The complete Next.js boilerplate for building profitable SaaS, with auth, payments, i18n, newsletter, dashboard, blog, docs, blocks, themes, SEO and more.
First, run the development server:
## Author
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
This project is created by [Fox](https://x.com/indie_maker_fox), the founder of [MkSaaS](https://mksaas.com) and [Mkdirs](https://mkdirs.com). The official X account for [MkSaaS](https://mksaas.com) is [@mksaascom](https://x.com/mksaascom), you can follow this account for the updates about MkSaaS.
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## Documentation
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
The documentation is available on the [website](https://mksaas.com/docs). It includes guides, tutorials, and detailed explanations of the code. I designed it to be as beginner-friendly as possible, so you can start making money from day one.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
If you found anything that could be improved, please let me know.
## Learn More
## Links
To learn more about Next.js, take a look at the following resources:
- 🔥 website: [mksaas.com](https://mksaas.com)
- 🌐 demo: [demo.mksaas.com](https://demo.mksaas.com)
- 📚 documentation: [mksaas.com/docs](https://mksaas.com/docs)
- 🗓️ roadmap: [mksaas roadmap](https://mksaas.link/roadmap)
- 👨‍💻 discord: [mksaas.link/discord](https://mksaas.link/discord)
- 📹 video: [mksaas.link/youtube](https://mksaas.link/youtube)
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
## Repositories
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
By default, you should have access to all 5 repositories. If you find that youre unable to access any of them, please dont hesitate to reach out to me, and Ill assist you in resolving the issue.
## Deploy on Vercel
- [mksaas-template (ready)](https://github.com/MkSaaSHQ/mksaas-template): https://demo.mksaas.com
- [mksaas-blog (ready)](https://github.com/MkSaaSHQ/mksaas-blog): https://mksaas.me
- [mksaas-haitang (ready)](https://github.com/MkSaaSHQ/mksaas-haitang): https://haitang.app
- [mksaas-outfit (ready)](https://github.com/MkSaaSHQ/mksaas-outfit)
- [mksaas-app (WIP)](https://github.com/MkSaaSHQ/mksaas-app): https://mksaas.app
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
## Notice
> If you have any questions, please [submit an issue](https://github.com/MkSaaSHQ/mksaas-template/issues/new), or contact me at [support@mksaas.com](mailto:support@mksaas.com), or join our [discord community](https://mksaas.link/discord) and ask for help there.
> If you want to receive notifications whenever code changes, please click `Watch` button in the top right.
> When submitting any content to the issues of the repository, please use **English** as the main Language, so that everyone can read it and help you, thank you for your supports.
## License
For any details on the license, please refer to the [License](LICENSE) file.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

View File

@ -9,28 +9,14 @@
"ignoreUnknown": true,
"ignore": [
".next/**",
".open-next/**",
".wrangler/**",
".cursor/**",
".claude/**",
".kiro/**",
".conductor/**",
".vscode/**",
".source/**",
".content-collections/**",
"node_modules/**",
"dist/**",
"build/**",
"src/db/**",
"tailwind.config.ts",
"src/components/ui/*.tsx",
"src/components/magicui/*.tsx",
"src/components/animate-ui/*.tsx",
"src/components/tailark/*.tsx",
"src/components/ai-elements/*.tsx",
"src/app/[[]locale]/preview/**",
"src/payment/types.ts",
"src/credits/types.ts",
"src/types/index.d.ts"
"tailwind.config.ts",
"src/db/schema.ts"
]
},
"formatter": {
@ -38,8 +24,7 @@
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 80,
"formatWithErrors": true,
"useEditorconfig": true
"formatWithErrors": true
},
"organizeImports": {
"enabled": true
@ -49,53 +34,27 @@
"rules": {
"recommended": true,
"suspicious": {
"noSparseArray": "off",
"noArrayIndexKey": "off",
"noExplicitAny": "off",
"noShadowRestrictedNames": "off"
},
"complexity": {
"noForEach": "off"
},
"correctness": {
"useExhaustiveDependencies": "off"
"noSparseArray": "off"
},
"style": {
"useTemplate": "off",
"noNonNullAssertion": "off",
"useShorthandArrayType": "off",
"useNodejsImportProtocol": "off"
"useShorthandArrayType": "off"
},
"a11y": {
"useValidAnchor": "off",
"noSvgWithoutTitle": "off",
"useKeyWithClickEvents": "off"
"useValidAnchor": "off"
}
},
"ignore": [
".next/**",
".open-next/**",
".wrangler/**",
".cursor/**",
".claude/**",
".conductor/**",
".kiro/**",
".vscode/**",
".source/**",
".content-collections/**",
"node_modules/**",
"dist/**",
"build/**",
"src/db/**",
"tailwind.config.ts",
"src/components/ui/*.tsx",
"src/components/magicui/*.tsx",
"src/components/animate-ui/*.tsx",
"src/components/tailark/*.tsx",
"src/components/ai-elements/*.tsx",
"src/app/[[]locale]/preview/**",
"src/payment/types.ts",
"src/credits/types.ts",
"src/types/index.d.ts"
"tailwind.config.ts",
"src/db/schema.ts"
]
},
"javascript": {

7483
cloudflare-env.d.ts vendored

File diff suppressed because it is too large Load Diff

340
content-collections.ts Normal file
View File

@ -0,0 +1,340 @@
import { DEFAULT_LOCALE, LOCALES } from "@/i18n/routing";
import { defineCollection, defineConfig, z } from "@content-collections/core";
import {
createDocSchema,
createMetaSchema,
transformMDX,
} from '@fumadocs/content-collections/configuration';
import path from "path";
/**
* Content Collections documentation
* 1. https://www.content-collections.dev/docs/quickstart/next
* 2. https://www.content-collections.dev/docs/configuration
* 3. https://www.content-collections.dev/docs/transform#join-collections
*/
/**
* Use Content Collections for Fumadocs
* https://fumadocs.vercel.app/docs/headless/content-collections
*/
const docs = defineCollection({
name: 'docs',
directory: 'content/docs',
include: '**/*.mdx',
schema: (z) => ({
...createDocSchema(z),
preview: z.string().optional(),
index: z.boolean().default(false),
}),
transform: transformMDX,
});
const metas = defineCollection({
name: 'meta',
directory: 'content/docs',
include: '**/meta**.json',
parser: 'json',
schema: createMetaSchema,
});
/**
* Helper function to extract locale and base name from filename
* Handles filename formats:
* - name -> locale: DEFAULT_LOCALE, base: name
* - name.zh -> locale: zh, base: name
*
* @param fileName Filename without extension (already has .mdx removed)
* @returns Object with locale and base name
*/
function extractLocaleAndBase(fileName: string): { locale: string; base: string } {
// Split filename into parts
const parts = fileName.split('.');
if (parts.length === 1) {
// Simple filename without locale: xxx
return { locale: DEFAULT_LOCALE, base: parts[0] };
} else if (parts.length === 2 && LOCALES.includes(parts[1])) {
// Filename with locale: xxx.zh
return { locale: parts[1], base: parts[0] };
} else {
// Unexpected format, use first part as base and default locale
console.warn(`Unexpected filename format: ${fileName}`);
return { locale: DEFAULT_LOCALE, base: parts[0] };
}
}
/**
* Blog Author collection
*
* Authors are identified by their slug across all languages
* New format: content/author/authorname.{locale}.mdx
* Example: content/author/mksaas.mdx (default locale) and content/author/mksaas.zh.mdx (Chinese)
*/
export const authors = defineCollection({
name: 'author',
directory: 'content/author',
include: '**/*.mdx',
schema: (z) => ({
slug: z.string(),
name: z.string(),
avatar: z.string(),
locale: z.enum(LOCALES as [string, ...string[]]).optional()
}),
transform: async (data, context) => {
// Get the filename from the path
const filePath = data._meta.path;
const fileName = filePath.split(path.sep).pop() || '';
// Extract locale and base from filename
const { locale, base } = extractLocaleAndBase(fileName);
// console.log(`author processed: ${fileName}, locale=${locale}`);
return {
...data,
locale,
};
}
});
/**
* Blog Category collection
*
* Categories are identified by their slug across all languages
* New format: content/category/categoryname.{locale}.mdx
* Example: content/category/tutorial.mdx (default locale) and content/category/tutorial.zh.mdx (Chinese)
*/
export const categories = defineCollection({
name: 'category',
directory: 'content/category',
include: '**/*.mdx',
schema: (z) => ({
slug: z.string(),
name: z.string(),
description: z.string(),
locale: z.enum(LOCALES as [string, ...string[]]).optional()
}),
transform: async (data, context) => {
// Get the filename from the path
const filePath = data._meta.path;
const fileName = filePath.split(path.sep).pop() || '';
// Extract locale and base from filename
const { locale, base } = extractLocaleAndBase(fileName);
// console.log(`category processed: ${fileName}, locale=${locale}`);
return {
...data,
locale
};
}
});
/**
* Blog Post collection
*
* New format: content/blog/post-slug.{locale}.mdx
*
* 1. For a blog post at content/blog/first-post.mdx (default locale):
* locale: en
* slug: /blog/first-post
* slugAsParams: first-post
*
* 2. For a blog post at content/blog/first-post.zh.mdx (Chinese locale):
* locale: zh
* slug: /blog/first-post
* slugAsParams: first-post
*/
export const posts = defineCollection({
name: 'post',
directory: 'content/blog',
include: '**/*.mdx',
schema: (z) => ({
title: z.string(),
description: z.string(),
image: z.string(),
date: z.string().datetime(),
published: z.boolean().default(true),
categories: z.array(z.string()),
author: z.string(),
locale: z.enum(LOCALES as [string, ...string[]]).optional(),
estimatedTime: z.number().optional() // Reading time in minutes
}),
transform: async (data, context) => {
// Use Fumadocs transformMDX for consistent MDX processing
const transformedData = await transformMDX(data, context);
// Get the filename from the path
const filePath = data._meta.path;
const fileName = filePath.split(path.sep).pop() || '';
// Extract locale and base from filename
const { locale, base } = extractLocaleAndBase(fileName);
// console.log(`post processed: ${fileName}, base=${base}, locale=${locale}`);
// Find the author by matching slug
const blogAuthor = context
.documents(authors)
.find((a) => a.slug === data.author && a.locale === locale) ||
context
.documents(authors)
.find((a) => a.slug === data.author);
// Find categories by matching slug
const blogCategories = data.categories.map(categorySlug => {
// Try to find a category with matching slug and locale
const category = context
.documents(categories)
.find(c => c.slug === categorySlug && c.locale === locale) ||
context
.documents(categories)
.find(c => c.slug === categorySlug);
return category;
}).filter(Boolean); // Remove null values
// Get the collection name (e.g., "blog")
const pathParts = data._meta.path.split(path.sep);
const collectionName = pathParts[pathParts.length - 2];
// Create the slug and slugAsParams
const slug = `/${collectionName}/${base}`;
const slugAsParams = base;
// Calculate estimated reading time
const wordCount = data.content.split(/\s+/).length;
const wordsPerMinute = 200; // average reading speed: 200 words per minute
const estimatedTime = Math.max(Math.ceil(wordCount / wordsPerMinute), 1);
return {
...data,
locale,
author: blogAuthor,
categories: blogCategories,
slug,
slugAsParams,
estimatedTime,
body: transformedData.body,
toc: transformedData.toc
};
}
});
/**
* Pages collection for policy pages like privacy-policy, terms-of-service, etc.
*
* New format: content/pages/page-slug.{locale}.mdx
*
* 1. For a page at content/pages/privacy-policy.mdx (default locale):
* locale: en
* slug: /pages/privacy-policy
* slugAsParams: privacy-policy
*
* 2. For a page at content/pages/privacy-policy.zh.mdx (Chinese locale):
* locale: zh
* slug: /pages/privacy-policy
* slugAsParams: privacy-policy
*/
export const pages = defineCollection({
name: 'page',
directory: 'content/pages',
include: '**/*.{md,mdx}',
schema: (z) => ({
title: z.string(),
description: z.string(),
date: z.string().datetime(),
published: z.boolean().default(true),
locale: z.enum(LOCALES as [string, ...string[]]).optional()
}),
transform: async (data, context) => {
// Use Fumadocs transformMDX for consistent MDX processing
const transformedData = await transformMDX(data, context);
// Get the filename from the path
const filePath = data._meta.path;
const fileName = filePath.split(path.sep).pop() || '';
// Extract locale and base from filename
const { locale, base } = extractLocaleAndBase(fileName);
// console.log(`page processed: ${fileName}, base=${base}, locale=${locale}`);
// Get the collection name (e.g., "pages")
const pathParts = data._meta.path.split(path.sep);
const collectionName = pathParts[pathParts.length - 2];
// Create the slug and slugAsParams
const slug = `/${collectionName}/${base}`;
const slugAsParams = base;
return {
...data,
locale,
slug,
slugAsParams,
body: transformedData.body,
toc: transformedData.toc
};
}
});
/**
* Releases collection for changelog
*
* New format: content/release/version-slug.{locale}.mdx
*
* 1. For a release at content/release/v1-0-0.mdx (default locale):
* locale: en
* slug: /release/v1-0-0
* slugAsParams: v1-0-0
*
* 2. For a release at content/release/v1-0-0.zh.mdx (Chinese locale):
* locale: zh
* slug: /release/v1-0-0
* slugAsParams: v1-0-0
*/
export const releases = defineCollection({
name: 'release',
directory: 'content/release',
include: '**/*.{md,mdx}',
schema: (z) => ({
title: z.string(),
description: z.string(),
date: z.string().datetime(),
version: z.string(),
published: z.boolean().default(true),
locale: z.enum(LOCALES as [string, ...string[]]).optional()
}),
transform: async (data, context) => {
// Use Fumadocs transformMDX for consistent MDX processing
const transformedData = await transformMDX(data, context);
// Get the filename from the path
const filePath = data._meta.path;
const fileName = filePath.split(path.sep).pop() || '';
// Extract locale and base from filename
const { locale, base } = extractLocaleAndBase(fileName);
// console.log(`release processed: ${fileName}, base=${base}, locale=${locale}`);
// Get the collection name (e.g., "release")
const pathParts = data._meta.path.split(path.sep);
const collectionName = pathParts[pathParts.length - 2];
// Create the slug and slugAsParams
const slug = `/${collectionName}/${base}`;
const slugAsParams = base;
return {
...data,
locale,
slug,
slugAsParams,
body: transformedData.body,
toc: transformedData.toc
};
}
});
export default defineConfig({
collections: [docs, metas, authors, categories, posts, pages, releases]
});

View File

@ -1,4 +0,0 @@
---
name: Fox
avatar: /images/avatars/fox.png
---

View File

@ -1,4 +0,0 @@
---
name: Fox
avatar: /images/avatars/fox.png
---

View File

@ -1,4 +1,5 @@
---
slug: mkdirs
name: Mkdirs
avatar: /images/avatars/mkdirs.png
---

View File

@ -1,4 +1,5 @@
---
name: Mkdirs模板
slug: mkdirs
name: Mkdirs
avatar: /images/avatars/mkdirs.png
---

View File

@ -1,4 +1,5 @@
---
slug: mksaas
name: MkSaaS
avatar: /images/avatars/mksaas.png
---

View File

@ -1,4 +1,5 @@
---
name: MkSaaS模板
slug: mksaas
name: MkSaaS
avatar: /images/avatars/mksaas.png
---

View File

@ -1,11 +1,11 @@
---
title: Comparisons
description: How is Fumadocs different from other existing frameworks?
image: /images/blog/post-2.png
date: "2025-03-22"
image: /images/blog/mkdirs-og.png
date: 2024-11-25T12:00:00.000Z
published: true
categories: [news, company]
author: fox
author: mkdirs
---
## Nextra

View File

@ -1,11 +1,11 @@
---
title: 对比
description: Fumadocs 与其他现有框架有何不同?
image: /images/blog/post-2.png
date: "2025-03-22"
image: /images/blog/mkdirs-og.png
date: 2024-11-25T12:00:00.000Z
published: true
categories: [news, company]
author: fox
author: mkdirs
---
## Nextra
@ -69,4 +69,4 @@ Docusaurus 是一个基于 React.js 的强大框架。它通过插件和自定
您可以通过插件轻松实现许多功能,他们的生态系统确实更大,并由许多贡献者维护。
相比之下Fumadocs 的灵活性允许您自己实现它们,可能需要更长的时间来调整它以达到您的满意度。
相比之下Fumadocs 的灵活性允许您自己实现它们,可能需要更长的时间来调整它以达到您的满意度。

View File

@ -1,278 +0,0 @@
---
title: Quick Start
description: Getting Started with Fumadocs
image: /images/blog/post-8.png
date: "2025-03-28"
published: true
categories: [company, news]
author: mksaas
---
## Introduction
Fumadocs <span className='text-fd-muted-foreground text-sm'>(Foo-ma docs)</span> is a **documentation framework** based on Next.js, designed to be fast, flexible,
and composes seamlessly into Next.js App Router.
Fumadocs has different parts:
<Cards>
<Card icon={<CpuIcon className="text-purple-300" />} title='Fumadocs Core'>
Handles most of the logic, including document search, content source adapters, and Markdown extensions.
</Card>
<Card icon={<PanelsTopLeft className="text-blue-300" />} title='Fumadocs UI'>
The default theme of Fumadocs offers a beautiful look for documentation sites and interactive components.
</Card>
<Card icon={<Database />} title='Content Source'>
The source of your content, can be a CMS or local data layers like [Content Collections](https://www.content-collections.dev) and [Fumadocs MDX](/docs/mdx), the official content source.
</Card>
<Card icon={<Terminal />} title='Fumadocs CLI'>
A command line tool to install UI components and automate things, useful for customizing layouts.
</Card>
</Cards>
<Callout title="Want to learn more?">
Read our in-depth [What is Fumadocs](/docs/what-is-fumadocs) introduction.
</Callout>
### Terminology
**Markdown/MDX:** Markdown is a markup language for creating formatted text. Fumadocs supports Markdown and MDX (superset of Markdown) out-of-the-box.
Although not required, some basic knowledge of Next.js App Router would be useful for further customisations.
## Automatic Installation
A minimum version of Node.js 18 required, note that Node.js 23.1 might have problems with Next.js production build.
<Tabs groupId='package-manager' persist items={['npm', 'pnpm', 'yarn', 'bun']}>
```bash tab="npm"
npm create fumadocs-app
```
```bash tab="pnpm"
pnpm create fumadocs-app
```
```bash tab="yarn"
yarn create fumadocs-app
```
```bash tab="bun"
bun create fumadocs-app
```
</Tabs>
It will ask you the framework and content source to use, a new fumadocs app should be initialized. Now you can start hacking!
<Callout title='From Existing Codebase?'>
You can follow the [Manual Installation](/docs/manual-installation) guide to get started.
</Callout>
### Enjoy!
Create your first MDX file in the docs folder.
```mdx title="content/docs/index.mdx"
---
title: Hello World
---
## Yo what's up
```
Run the app in development mode and see http://localhost:3000/docs.
```mdx
npm run dev
```
## Explore
In the project, you can see:
- `lib/source.ts`: Code for content source adapter, [`loader()`](/docs/headless/source-api) provides an interface to interact with your content source, and assigns URL to your pages.
- `app/layout.config.tsx`: Shared options for layouts, optional but preferred to keep.
| Route | Description |
| ------------------------- | ------------------------------------------------------ |
| `app/(home)` | The route group for your landing page and other pages. |
| `app/docs` | The documentation layout and pages. |
| `app/api/search/route.ts` | The Route Handler for search. |
### Writing Content
For authoring docs, make sure to read:
<Cards>
<Card href="/docs/markdown" title="Markdown">
Fumadocs has some additional features for authoring content too.
</Card>
<Card href="/docs/navigation" title="Navigation">
Learn how to customise navigation links/sidebar items.
</Card>
</Cards>
### Content Source
Content source handles all your content, like compiling Markdown files and validating frontmatter.
<Tabs items={['Fumadocs MDX', 'Custom Source']}>
<Tab value='Fumadocs MDX'>
Read the [Introduction](/docs/mdx) to learn how it handles your content.
A `source.config.ts` config file has been included, you can customise different options like frontmatter schema.
</Tab>
<Tab value='Custom Source'>
Fumadocs is not Markdown-exclusive. For other sources like Sanity, you can build a [custom content source](/docs/headless/custom-source).
</Tab>
</Tabs>
### Customise UI
See [Customisation Guide](/docs/customisation).
## FAQ
Some common questions you may encounter.
<Accordions>
<Accordion id='fix-monorepo-styling' title="How to fix stylings not being applied in Monorepo?">
Sometimes, `fumadocs-ui` is not installed in the workspace of your Tailwind CSS configuration file. (e.g. a monorepo setup).
You have to ensure the `fumadocs-ui` package is scanned by Tailwind CSS, and give a correct relative path to `@source`.
For example, add `../../` to point to the `node_modules` folder in root workspace.
```css
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
/* [!code --] */
@source '../node_modules/fumadocs-ui/dist/**/*.js';
/* [!code ++] */
@source '../../../node_modules/fumadocs-ui/dist/**/*.js';
```
</Accordion>
<Accordion id='change-base-url' title="How to change the base route of /docs?">
You can change the base route of docs (e.g. from `/docs/page` to `/info/page`).
Since Fumadocs uses Next.js App Router, you can simply rename the route:
<Files>
<Folder name="app/docs" defaultOpen className="opacity-50" disabled>
<File name="layout.tsx" />
</Folder>
<Folder name="app/info" defaultOpen>
<File name="layout.tsx" />
</Folder>
</Files>
And tell Fumadocs to use the new route in `source.ts`:
```ts title="lib/source.ts"
import { loader } from 'fumadocs-core/source';
export const source = loader({
baseUrl: '/info',
// other options
});
```
</Accordion>
<Accordion id='dynamic-route' title="It uses Dynamic Route, will it be poor in performance?">
Next.js turns dynamic route into static routes when `generateStaticParams` is configured.
Hence, it is as fast as static pages.
You can enable Static Exports on Next.js to get a static build output. (Notice that Route Handler doesn't work with static export, you have to configure static search)
</Accordion>
<Accordion id='custom-layout-docs-page' title='How to create a page in /docs without docs layout?'>
Same as managing layouts in Next.js App Router, remove the original MDX file from content directory (`/content/docs`).
This ensures duplicated pages will not cause errors.
Now, You can add the page to another route group, which isn't a descendant of docs layout.
For example, under your `app` folder:
<Files>
<File name="(home)/docs/page.tsx" />
<Folder name="docs">
<File name="layout.tsx" />
<File name="[[...slug]]/page.tsx" />
</Folder>
</Files>
will replace the `/docs` page with your `page.tsx`.
</Accordion>
<Accordion id='multi-versions' title="How to implement docs with multi-version?">
Use a separate deployment for each version.
On Vercel, this can be done by creating another branch for a specific version on your GitHub repository.
To link to the sites of other versions, use the Links API or a custom navigation component.
</Accordion>
<Accordion id='multi-docs' title="How to implement multi-docs?">
We recommend to use [Sidebar Tabs](/docs/navigation/sidebar#sidebar-tabs).
</Accordion>
</Accordions>
## Learn More
New to here? Don't worry, we are welcome for your questions.
If you find anything confusing, please give your feedback on [Github Discussion](https://github.com/fuma-nama/fumadocs/discussions)!
<Cards>
<Card
href="/docs/static-export"
title="Configure Static Export"
description="Learn how to enable static export on your docs"
/>
<Card
href="/docs/search"
title="Customise Search"
description="Learn how to customise document search"
/>
<Card
href="/docs/theme"
title="Theming"
description="Add themes to Fumadocs UI"
/>
<Card
href="/docs/components"
title="Components"
description="See all available components to enhance your docs"
/>
</Cards>

View File

@ -1,253 +0,0 @@
---
title: 快速入门
description: Fumadocs 入门指南
image: /images/blog/post-8.png
date: "2025-03-28"
published: true
categories: [company, news]
author: mksaas
---
## 简介
Fumadocs <span className='text-fd-muted-foreground text-sm'>(Foo-ma docs)</span> 是一个基于 Next.js 的**文档框架**,设计为快速、灵活,
并无缝集成到 Next.js App Router 中。
Fumadocs 由不同部分组成:
<Cards>
<Card icon={<CpuIcon className="text-purple-300" />} title='Fumadocs Core'>
处理大部分逻辑,包括文档搜索、内容源适配器和 Markdown 扩展。
</Card>
<Card icon={<PanelsTopLeft className="text-blue-300" />} title='Fumadocs UI'>
Fumadocs 的默认主题为文档站点提供了美观的外观和交互式组件。
</Card>
<Card icon={<Database />} title='Content Source'>
您内容的来源,可以是 CMS 或本地数据层,如 [Content Collections](https://www.content-collections.dev) 和 [Fumadocs MDX](/docs/mdx),即官方内容源。
</Card>
<Card icon={<Terminal />} title='Fumadocs CLI'>
一个命令行工具,用于安装 UI 组件和自动化操作,对于自定义布局非常有用。
</Card>
</Cards>
<Callout title="想了解更多?">
阅读我们深入的 [什么是 Fumadocs](/docs/what-is-fumadocs) 介绍。
</Callout>
### 术语
**Markdown/MDX:** Markdown 是一种用于创建格式化文本的标记语言。Fumadocs 默认支持 Markdown 和 MDXMarkdown 的超集)。
虽然不是必需的,但对 Next.js App Router 的基本了解对于进一步的自定义会很有帮助。
## 自动安装
需要 Node.js 18 或更高版本,请注意 Node.js 23.1 可能在 Next.js 生产构建中存在问题。
<Tabs groupId='package-manager' persist items={['npm', 'pnpm', 'yarn', 'bun']}>
```bash tab="npm"
npm create fumadocs-app
```
```bash tab="pnpm"
pnpm create fumadocs-app
```
```bash tab="yarn"
yarn create fumadocs-app
```
```bash tab="bun"
bun create fumadocs-app
```
</Tabs>
它会询问您要使用的框架和内容源,随后将初始化一个新的 fumadocs 应用程序。现在您可以开始动手了!
<Callout title='从现有代码库开始?'>
您可以按照 [手动安装](/docs/manual-installation) 指南开始。
</Callout>
### 尽情使用!
在 docs 文件夹中创建您的第一个 MDX 文件。
```mdx title="content/docs/index.mdx"
---
title: Hello World
---
## Yo what's up
```
在开发模式下运行应用程序并查看 http://localhost:3000/docs。
```mdx
npm run dev
```
## 探索
在项目中,您可以看到:
- `lib/source.ts`:内容源适配器的代码,[`loader()`](/docs/headless/source-api) 提供了与内容源交互的接口,并为您的页面分配 URL。
- `app/layout.config.tsx`:布局的共享选项,可选但建议保留。
| 路由 | 描述 |
| ------------------------- | -------------------------------------- |
| `app/(home)` | 您的登陆页面和其他页面的路由组。 |
| `app/docs` | 文档布局和页面。 |
| `app/api/search/route.ts` | 搜索的路由处理器。 |
### 编写内容
对于编写文档,请务必阅读:
<Cards>
<Card href="/docs/markdown" title="Markdown">
Fumadocs 还有一些额外的内容创作功能。
</Card>
<Card href="/docs/navigation" title="Navigation">
了解如何自定义导航链接/侧边栏项目。
</Card>
</Cards>
### 内容源
内容源处理您的所有内容,例如编译 Markdown 文件和验证前言。
<Tabs items={['Fumadocs MDX', 'Custom Source']}>
<Tab value='Fumadocs MDX'>
阅读 [介绍](/docs/mdx) 了解它如何处理您的内容。
项目中已包含 `source.config.ts` 配置文件,您可以自定义不同的选项,如前言模式。
</Tab>
<Tab value='Custom Source'>
Fumadocs 不仅限于 Markdown。对于其他源如 Sanity您可以构建 [自定义内容源](/docs/headless/custom-source)。
</Tab>
</Tabs>
### 自定义 UI
请参阅 [自定义指南](/docs/customisation)。
## 常见问题
您可能遇到的一些常见问题。
<Accordions>
<Accordion id='fix-monorepo-styling' title="如何修复 Monorepo 中样式不应用的问题?">
有时,`fumadocs-ui` 没有安装在您的 Tailwind CSS 配置文件的工作区中(例如,在 monorepo 设置中)。
您必须确保 Tailwind CSS 扫描 `fumadocs-ui` 包,并为 `@source` 提供正确的相对路径。
例如,添加 `../../` 指向根工作区中的 `node_modules` 文件夹。
```css
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
/* [!code --] */
@source '../node_modules/fumadocs-ui/dist/**/*.js';
/* [!code ++] */
@source '../../../node_modules/fumadocs-ui/dist/**/*.js';
```
</Accordion>
<Accordion id='change-base-url' title="如何更改 /docs 的基本路由?">
您可以更改文档的基本路由(例如,从 `/docs/page` 更改为 `/info/page`)。
由于 Fumadocs 使用 Next.js App Router您可以简单地重命名路由
<Files>
<Folder name="app/docs" defaultOpen className="opacity-50" disabled>
<File name="layout.tsx" />
</Folder>
<Folder name="app/info" defaultOpen>
<File name="layout.tsx" />
</Folder>
</Files>
并在 `source.ts` 中告诉 Fumadocs 使用新的路由:
```ts title="lib/source.ts"
import { loader } from 'fumadocs-core/source';
export const source = loader({
baseUrl: '/info',
// other options
});
```
</Accordion>
<Accordion id='dynamic-route' title="它使用动态路由,性能会很差吗?">
当配置了 `generateStaticParams` 时Next.js 会将动态路由转换为静态路由。
因此,它与静态页面一样快。
您可以在 Next.js 上启用静态导出,获得静态构建输出。(请注意,路由处理器不适用于静态导出,您必须配置静态搜索)
</Accordion>
<Accordion id='custom-layout-docs-page' title='如何在 /docs 中创建没有文档布局的页面?'>
与在 Next.js App Router 中管理布局相同,从内容目录(`/content/docs`)中删除原始 MDX 文件。
这确保重复的页面不会导致错误。
现在,您可以将页面添加到另一个路由组,该组不是文档布局的后代。
例如,在您的 `app` 文件夹下:
<Files>
<File name="(home)/docs/page.tsx" />
<Folder name="docs">
<File name="layout.tsx" />
<File name="[[...slug]]/page.tsx" />
</Folder>
</Files>
将用您的 `page.tsx` 替换 `/docs` 页面。
</Accordion>
<Accordion id='multi-versions' title="如何实现多版本文档?">
为每个版本使用单独的部署。
在 Vercel 上,可以通过在 GitHub 存储库中为特定版本创建另一个分支来实现。
要链接到其他版本的站点,请使用 Links API 或自定义导航组件。
</Accordion>
<Accordion id='multi-docs' title="如何实现多文档?">
我们建议使用 [侧边栏标签](/docs/navigation/sidebar#sidebar-tabs)。
</Accordion>
</Accordions>
## 了解更多
刚来这里?别担心,我们欢迎您的问题。

View File

@ -1,8 +1,8 @@
---
title: Internationalization
description: Support multiple languages in your documentation
image: /images/blog/post-3.png
date: "2025-03-15"
image: /images/blog/mksaas-og.png
date: 2024-11-26T12:00:00.000Z
published: true
categories: [company, product]
author: mksaas

View File

@ -1,8 +1,8 @@
---
title: 国际化
description: 在您的文档中支持多种语言
image: /images/blog/post-3.png
date: "2025-03-15"
image: /images/blog/mksaas-og.png
date: 2024-11-26T12:00:00.000Z
published: true
categories: [company, product]
author: mksaas
@ -224,4 +224,4 @@ return <Link href={`/${lang}/another-page`}>This is a link</Link>;
import { DynamicLink } from 'fumadocs-core/dynamic-link';
<DynamicLink href="/[lang]/another-page">This is a link</DynamicLink>
```
```

View File

@ -1,11 +1,11 @@
---
title: Manual Installation
description: Create a new fumadocs project from scratch.
image: /images/blog/post-4.png
date: "2025-03-14"
image: /images/blog/mksaas-og.png
date: 2025-03-31T12:00:00.000Z
published: true
categories: [company, product]
author: mkdirs
author: mksaas
---
> Read the [Quick Start](/docs) guide first for basic concept.
@ -14,7 +14,7 @@ author: mkdirs
Create a new Next.js application with `create-next-app`, and install required packages.
```mdx
```package-install
fumadocs-ui fumadocs-core
```
@ -103,9 +103,8 @@ Create a catch-all route `/app/docs/[[...slug]]` for docs pages.
In the page, wrap your content in the [Page](/docs/layouts/page) component.
It may vary depending on your content source. You should configure static rendering with `generateStaticParams` and metadata with `generateMetadata`.
<Tabs items={['Fumadocs MDX', 'Content Collections']}>
<Tabs groupId='content-source' items={['Fumadocs MDX', 'Content Collections']}>
<Tab value='Fumadocs MDX'>
```json doc-gen:file
{
"file": "../../examples/next-mdx/app/docs/[[...slug]]/page.tsx",
@ -114,9 +113,7 @@ It may vary depending on your content source. You should configure static render
}
}
```
</Tab>
<Tab value='Content Collections'>
```json doc-gen:file
{
"file": "../../examples/content-collections/app/docs/[[...slug]]/page.tsx",
@ -125,7 +122,6 @@ It may vary depending on your content source. You should configure static render
}
}
```
</Tab>
</Tabs>
@ -133,9 +129,8 @@ It may vary depending on your content source. You should configure static render
Use the default document search based on Orama.
<Tabs items={['Fumadocs MDX', 'Content Collections']}>
<Tabs groupId='content-source' items={['Fumadocs MDX', 'Content Collections']}>
<Tab value='Fumadocs MDX'>
```json doc-gen:file
{
"file": "../../examples/next-mdx/app/api/search/route.ts",
@ -144,9 +139,7 @@ Use the default document search based on Orama.
}
}
```
</Tab>
<Tab value='Content Collections'>
```json doc-gen:file
{
"file": "../../examples/content-collections/app/api/search/route.ts",
@ -155,7 +148,6 @@ Use the default document search based on Orama.
}
}
```
</Tab>
</Tabs>

View File

@ -1,11 +1,11 @@
---
title: 手动安装
description: 从零开始创建一个新的 Fumadocs 项目
image: /images/blog/post-4.png
date: "2025-03-14"
image: /images/blog/mksaas-og.png
date: 2025-03-31T12:00:00.000Z
published: true
categories: [company, product]
author: mkdirs
author: mksaas
---
> 请先阅读[快速入门](/docs)指南了解基本概念。
@ -14,7 +14,7 @@ author: mkdirs
使用 `create-next-app` 创建一个新的 Next.js 应用程序,并安装所需的包。
```mdx
```package-install
fumadocs-ui fumadocs-core
```
@ -103,9 +103,8 @@ export default function Layout({ children }: { children: ReactNode }) {
在页面中,将您的内容包装在 [Page](/docs/layouts/page) 组件中。
这可能因您的内容源而异。您应该使用 `generateStaticParams` 配置静态渲染,并使用 `generateMetadata` 配置元数据。
<Tabs items={['Fumadocs MDX', 'Content Collections']}>
<Tabs groupId='content-source' items={['Fumadocs MDX', 'Content Collections']}>
<Tab value='Fumadocs MDX'>
```json doc-gen:file
{
"file": "../../examples/next-mdx/app/docs/[[...slug]]/page.tsx",
@ -114,9 +113,7 @@ export default function Layout({ children }: { children: ReactNode }) {
}
}
```
</Tab>
<Tab value='Content Collections'>
```json doc-gen:file
{
"file": "../../examples/content-collections/app/docs/[[...slug]]/page.tsx",
@ -125,16 +122,15 @@ export default function Layout({ children }: { children: ReactNode }) {
}
}
```
</Tab>
</Tabs>
### 搜索
使用基于 Orama 的默认文档搜索。
<Tabs items={['Fumadocs MDX', 'Content Collections']}>
<Tabs groupId='content-source' items={['Fumadocs MDX', 'Content Collections']}>
<Tab value='Fumadocs MDX'>
```json doc-gen:file
{
"file": "../../examples/next-mdx/app/api/search/route.ts",
@ -143,9 +139,7 @@ export default function Layout({ children }: { children: ReactNode }) {
}
}
```
</Tab>
<Tab value='Content Collections'>
```json doc-gen:file
{
"file": "../../examples/content-collections/app/api/search/route.ts",
@ -154,7 +148,7 @@ export default function Layout({ children }: { children: ReactNode }) {
}
}
```
</Tab>
</Tabs>
了解更多关于[文档搜索](/docs/headless/search)的信息。
@ -193,4 +187,4 @@ WORKDIR /app
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* source.config.ts ./
```
这确保 Fumadocs MDX 在构建期间可以访问您的配置文件。
这确保 Fumadocs MDX 在构建期间可以访问您的配置文件。

View File

@ -1,8 +1,8 @@
---
title: Markdown
description: How to write documents
image: /images/blog/post-5.png
date: "2025-03-05"
image: /images/blog/mkdirs-og.png
date: 2024-11-25T12:00:00.000Z
published: true
categories: [news, company]
author: mkdirs
@ -298,34 +298,24 @@ You can use code blocks with the `<Tab />` component.
````mdx
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
<Tabs items={['Tab 1', 'Tab 2']}>
<Tab value='Tab 1'>
```ts
console.log('A');
```
</Tab>
<Tab value='Tab 2'>
```ts
console.log('B');
```
</Tab>
</Tabs>
```ts tab="Tab 1"
console.log('A');
```
```ts tab="Tab 2"
console.log('B');
```
````
> Note that you can add MDX components instead of importing them in MDX files.
<Tabs items={['Tab 1', 'Tab 2']}>
<Tab value='Tab 1'>
```ts
console.log('A');
```
</Tab>
<Tab value='Tab 2'>
```ts
console.log('B');
```
</Tab>
</Tabs>
```ts tab="Tab 1"
console.log('A');
```
```ts tab="Tab 2"
console.log('B');
```
### Using Typescript Twoslash
@ -342,8 +332,6 @@ Images are automatically optimized for `next/image`.
![Image](/image.png)
```
![Image](/images/blog/post-1.png)
## Optional
Some optional plugins you can enable.
@ -353,12 +341,12 @@ Some optional plugins you can enable.
Write math equations with TeX.
````md
```mdx
```math
f(x) = x * e^{2 pi i \xi x}
```
````
```mdx
```math
f(x) = x * e^{2 pi i \xi x}
```
@ -369,12 +357,12 @@ To enable, see [Math Integration](/docs/math).
Generate code blocks for installing packages via package managers (JS/Node.js).
````md
```mdx
```package-install
npm i next -D
```
````
```mdx
```package-install
npm i next -D
```

View File

@ -1,8 +1,8 @@
---
title: Markdown
description: 如何撰写文档
image: /images/blog/post-5.png
date: "2025-03-05"
image: /images/blog/mkdirs-og.png
date: 2024-11-25T12:00:00.000Z
published: true
categories: [news, company]
author: mkdirs
@ -251,95 +251,4 @@ console.log('Hello World');
```
````
### 高亮行
````md
```tsx
<div>Hello World</div> // [\!code highlight]
<div>Hello World</div>
<div>Goodbye</div>
<div>Hello World</div>
```
````
### 高亮单词
您可以通过添加 `[!code word:<match>]` 来高亮特定单词。
````md
```js
// [\!code word:config]
const config = {
reactStrictMode: true,
};
```
````
### 差异
````mdx
```ts
console.log('hewwo'); // [\!code --]
console.log('hello'); // [\!code ++]
```
````
```ts
console.log('hewwo'); // [!code --]
console.log('hello'); // [!code ++]
```
### 标签组
您可以使用 `<Tab />` 组件与代码块一起使用。
````mdx
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
<Tabs items={['Tab 1', 'Tab 2']}>
<Tab value='Tab 1'>
```ts
console.log('A');
```
</Tab>
<Tab value='Tab 2'>
```ts
console.log('B');
```
</Tab>
</Tabs>
````
> 注意,您可以在 MDX 文件中添加 MDX 组件,而不必导入它们。
<Tabs items={['Tab 1', 'Tab 2']}>
<Tab value='Tab 1'>
```ts
console.log('A');
```
</Tab>
<Tab value='Tab 2'>
```ts
console.log('B');
```
</Tab>
</Tabs>
### 使用 Typescript Twoslash
编写带有悬停类型信息和检测到类型错误的 Typescript 代码块。
默认情况下未启用。参见 [Twoslash](/docs/twoslash)。
## 图片
所有内置内容源都能正确处理图片。
图片会自动为 `next/image` 优化。
```mdx
![Image](/image.png)
```
## 可选功能
一些您可以启用的可选插件。
### 高亮行

View File

@ -1,56 +0,0 @@
---
title: "Premium Blog Post"
description: "This blog post is a test for premium content."
date: "2025-08-30"
published: true
premium: true
categories: ["product"]
author: "fox"
image: "/images/blog/post-7.png"
---
This blog post is a test for premium content.
You can read this part of the blog post if you are not a premium user.
But for the rest of the blog post, you need to be logged in as a premium user.
You can click the "Sign In" button to sign in as a user with free plan.
Then you can click the "Upgrade Now" button to upgrade to a premium plan.
<Callout type="warn">
Don't worry, you don't actually pay any cents, because we are in the sandbox environment of Stripe.
</Callout>
You can use the test card number to pay for monthly or yearly PRO plan or LIFETIME plan.
```
Card number: 4242 4242 4242 4242
Exp: 12/34
CVV: 567
```
After that, you can return to the blog post and you can read the rest of the blog post.
For more details, please check out the documentation: [Blog](https://mksaas.com/docs/blog).
Now the rest of the blog post is premium content.
<PremiumContent>
<Callout type="info">
This is the beginning of the premium content part.
</Callout>
This is the premium content part.
You can read this paragraph only if you are a premium user.
Please don't share this blog post with others.
<Callout type="info">
This is the end of the premium content part.
</Callout>
</PremiumContent>

View File

@ -1,56 +0,0 @@
---
title: "测试专用付费文章"
description: "这是一篇测试专用付费文章。"
date: "2025-08-30"
published: true
premium: true
categories: ["product"]
author: "fox"
image: "/images/blog/post-7.png"
---
这是一篇测试专用的付费文章。
如果你不是付费用户,你可以阅读这篇文章的这部分内容。
但如果你想阅读剩下的内容,你需要成为一个付费用户。
你可以点击 "登录" 按钮来以免费用户的身份登录。
然后你可以点击 "立即升级" 按钮来升级到付费计划。
<Callout type="warn">
不用担心,你实际上不需要支付任何费用,因为我们处于 Stripe 的沙盒环境中。
</Callout>
你可以使用测试卡号来支付月度或年度 PRO 计划或终身计划。
```
Card number: 4242 4242 4242 4242
Exp: 12/34
CVV: 567
```
之后,你可以返回这篇博客文章,然后你可以阅读剩下的内容。
更多详情,请参考文档:[博客](https://mksaas.com/docs/blog)。
现在剩下的内容是付费内容。
<PremiumContent>
<Callout type="info">
这是付费内容部分的开始。
</Callout>
这是付费内容部分。
你可以阅读这篇内容,只要你是一个付费用户。
请不要分享这篇文章给其他人。
<Callout type="info">
这是付费内容部分的结束。
</Callout>
</PremiumContent>

View File

@ -1,280 +0,0 @@
---
title: Search
description: Implement document search in your docs
image: /images/blog/post-6.png
date: "2025-02-15"
published: true
categories: [company, news]
author: mksaas
---
Fumadocs UI provides a good-looking search UI for your docs, the search functionality is instead provided and documented on Fumadocs Core.
See [Document Search](/docs/headless/search).
## Search UI
Open with <kbd>⌘</kbd> <kbd>K</kbd> or <kbd>Ctrl</kbd> <kbd>K</kbd>.
### Configurations
You can customize search UI from the [Root Provider](/docs/layouts/root-provider) component in root layout.
When not specified, it uses the Default [`fetch` Search Client](/docs/headless/search/orama) powered by Orama.
### Custom Links
Add custom link items to search dialog.
They are shown as fallbacks when the query is empty.
```tsx title="app/layout.tsx"
import { RootProvider } from 'fumadocs-ui/root-provider';
<RootProvider
search={{
links: [
['Home', '/'],
['Docs', '/docs'],
],
}}
>
{children}
</RootProvider>;
```
### Disable Search
To opt-out of document search, disable it from root provider.
```tsx
import { RootProvider } from 'fumadocs-ui/root-provider';
<RootProvider
search={{
enabled: false,
}}
>
{children}
</RootProvider>;
```
### Hot Keys
Customise the hot keys to trigger search dialog.
```tsx
import { RootProvider } from 'fumadocs-ui/root-provider';
<RootProvider
search={{
hotKey: [
{
display: 'K',
key: 'k', // key code, or a function determining whether the key is pressed
},
],
}}
>
{children}
</RootProvider>;
```
### Tag Filter
Add UI to change filters.
Make sure to configure [Tag Filter](/docs/headless/search/orama#tag-filter) on search server first.
```tsx
import { RootProvider } from 'fumadocs-ui/root-provider';
<RootProvider
search={{
options: {
defaultTag: 'value',
tags: [
{
name: 'Tag Name',
value: 'value',
},
],
},
}}
>
{children}
</RootProvider>;
```
### Search Options
Pass options to the search client, like changing the API endpoint for Orama search server:
```tsx
import { RootProvider } from 'fumadocs-ui/root-provider';
<RootProvider
search={{
options: {
api: '/api/search/docs',
},
}}
>
{children}
</RootProvider>;
```
### Replace Search Dialog
You can replace the default Search Dialog with:
```tsx title="components/search.tsx"
'use client';
import SearchDialog from 'fumadocs-ui/components/dialog/search-default';
import type { SharedProps } from 'fumadocs-ui/components/dialog/search';
export default function CustomDialog(props: SharedProps) {
// your own logic here
return <SearchDialog {...props} />;
}
```
To pass it to the Root Provider, you need a wrapper with `use client` directive.
```tsx title="provider.tsx"
'use client';
import { RootProvider } from 'fumadocs-ui/provider';
import dynamic from 'next/dynamic';
import type { ReactNode } from 'react';
const SearchDialog = dynamic(() => import('@/components/search')); // lazy load
export function Provider({ children }: { children: ReactNode }) {
return (
<RootProvider
search={{
SearchDialog,
}}
>
{children}
</RootProvider>
);
}
```
Use it instead of your previous Root Provider
```tsx title="layout.tsx"
import { Provider } from './provider';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>
<Provider>{children}</Provider>
</body>
</html>
);
}
```
## Other Solutions
### Algolia
For the setup guide, see [Integrate Algolia Search](/docs/headless/search/algolia).
While generally we recommend building your own search with their client-side
SDK, you can also plug the built-in dialog interface.
```tsx title="components/search.tsx"
'use client';
import algo from 'algoliasearch/lite';
import type { SharedProps } from 'fumadocs-ui/components/dialog/search';
import SearchDialog from 'fumadocs-ui/components/dialog/search-algolia';
const client = algo('appId', 'apiKey');
const index = client.initIndex('indexName');
export default function CustomSearchDialog(props: SharedProps) {
return <SearchDialog index={index} {...props} />;
}
```
1. Replace `appId`, `apiKey` and `indexName` with your desired values.
2. [Replace the default search dialog](#replace-search-dialog) with your new component.
<Callout title="Note" className='mt-4'>
The built-in implementation doesn't use instant search (their official
javascript client).
</Callout>
#### Tag Filter
Same as default search client, you can configure [Tag Filter](/docs/headless/search/algolia#tag-filter) on the dialog.
```tsx title="components/search.tsx"
import SearchDialog from 'fumadocs-ui/components/dialog/search-algolia';
<SearchDialog
defaultTag="value"
tags={[
{
name: 'Tag Name',
value: 'value',
},
]}
/>;
```
### Orama Cloud
For the setup guide, see [Integrate Orama Cloud](/docs/headless/search/orama-cloud).
```tsx title="components/search.tsx"
'use client';
import { OramaClient } from '@oramacloud/client';
import type { SharedProps } from 'fumadocs-ui/components/dialog/search';
import SearchDialog from 'fumadocs-ui/components/dialog/search-orama';
const client = new OramaClient({
endpoint: 'endpoint',
api_key: 'apiKey',
});
export default function CustomSearchDialog(props: SharedProps) {
return <SearchDialog {...props} client={client} showOrama />;
}
```
1. Replace `endpoint`, `apiKey` with your desired values.
2. [Replace the default search dialog](#replace-search-dialog) with your new component.
### Community Integrations
A list of integrations maintained by community.
- [Trieve Search](/docs/headless/search/trieve)
## Built-in UI
If you want to use the built-in search dialog UI instead of building your own,
you may use the `SearchDialog` component.
```tsx
import {
SearchDialog,
type SharedProps,
} from 'fumadocs-ui/components/dialog/search';
export default function CustomSearchDialog(props: SharedProps) {
return <SearchDialog {...props} />;
}
```
<Callout type="warn" title="Unstable">
It is an internal API, might break during iterations
</Callout>

View File

@ -1,252 +0,0 @@
---
title: 搜索
description: 在您的文档中实现文档搜索
image: /images/blog/post-6.png
date: "2025-02-15"
published: true
categories: [company, news]
author: mksaas
---
Fumadocs UI 为您的文档提供了一个美观的搜索界面,而搜索功能则由 Fumadocs Core 提供和记录。
参见[文档搜索](/docs/headless/search)。
## 搜索 UI
使用 <kbd>⌘</kbd> <kbd>K</kbd> 或 <kbd>Ctrl</kbd> <kbd>K</kbd> 打开。
### 配置
您可以通过根布局中的 [Root Provider](/docs/layouts/root-provider) 组件自定义搜索 UI。
当未指定时,它使用由 Orama 提供支持的默认 [`fetch` 搜索客户端](/docs/headless/search/orama)。
### 自定义链接
向搜索对话框添加自定义链接项。
当查询为空时,它们会显示为备选项。
```tsx title="app/layout.tsx"
import { RootProvider } from 'fumadocs-ui/root-provider';
<RootProvider
search={{
links: [
['Home', '/'],
['Docs', '/docs'],
],
}}
>
{children}
</RootProvider>;
```
### 禁用搜索
要禁用文档搜索,请在根提供程序中禁用它。
```tsx
import { RootProvider } from 'fumadocs-ui/root-provider';
<RootProvider
search={{
enabled: false,
}}
>
{children}
</RootProvider>;
```
### 热键
自定义触发搜索对话框的热键。
```tsx
import { RootProvider } from 'fumadocs-ui/root-provider';
<RootProvider
search={{
hotKey: [
{
display: 'K',
key: 'k', // key code, or a function determining whether the key is pressed
},
],
}}
>
{children}
</RootProvider>;
```
### 标签过滤器
添加 UI 以更改过滤器。
确保首先在搜索服务器上配置[标签过滤器](/docs/headless/search/orama#tag-filter)。
```tsx
import { RootProvider } from 'fumadocs-ui/root-provider';
<RootProvider
search={{
options: {
defaultTag: 'value',
tags: [
{
name: 'Tag Name',
value: 'value',
},
],
},
}}
>
{children}
</RootProvider>;
```
### 搜索选项
向搜索客户端传递选项,例如更改 Orama 搜索服务器的 API 端点:
```tsx
import { RootProvider } from 'fumadocs-ui/root-provider';
<RootProvider
search={{
options: {
api: '/api/search/docs',
},
}}
>
{children}
</RootProvider>;
```
### 替换搜索对话框
您可以用以下内容替换默认搜索对话框:
```tsx title="components/search.tsx"
'use client';
import SearchDialog from 'fumadocs-ui/components/dialog/search-default';
import type { SharedProps } from 'fumadocs-ui/components/dialog/search';
export default function CustomDialog(props: SharedProps) {
// your own logic here
return <SearchDialog {...props} />;
}
```
要将其传递给 Root Provider您需要一个带有 `use client` 指令的包装器。
```tsx title="provider.tsx"
'use client';
import { RootProvider } from 'fumadocs-ui/provider';
import dynamic from 'next/dynamic';
import type { ReactNode } from 'react';
const SearchDialog = dynamic(() => import('@/components/search')); // lazy load
export function Provider({ children }: { children: ReactNode }) {
return (
<RootProvider
search={{
SearchDialog,
}}
>
{children}
</RootProvider>
);
}
```
使用它替代您之前的 Root Provider
```tsx title="layout.tsx"
import { Provider } from './provider';
import type { ReactNode } from 'react';
export default function Layout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>
<Provider>{children}</Provider>
</body>
</html>
);
}
```
## 其他解决方案
### Algolia
关于设置指南,请参见[集成 Algolia 搜索](/docs/headless/search/algolia)。
虽然我们通常建议使用他们的客户端 SDK 构建您自己的搜索,但您也可以插入内置的对话框接口。
```tsx title="components/search.tsx"
'use client';
import algo from 'algoliasearch/lite';
import type { SharedProps } from 'fumadocs-ui/components/dialog/search';
import SearchDialog from 'fumadocs-ui/components/dialog/search-algolia';
const client = algo('appId', 'apiKey');
const index = client.initIndex('indexName');
export default function CustomSearchDialog(props: SharedProps) {
return <SearchDialog index={index} {...props} />;
}
```
1. 将 `appId`、`apiKey` 和 `indexName` 替换为您想要的值。
2. 用您的新组件[替换默认搜索对话框](#replace-search-dialog)。
<Callout title="注意" className='mt-4'>
内置实现不使用即时搜索(他们的官方 JavaScript 客户端)。
</Callout>
#### 标签过滤器
与默认搜索客户端相同,您可以在对话框上配置[标签过滤器](/docs/headless/search/algolia#tag-filter)。
```tsx title="components/search.tsx"
import SearchDialog from 'fumadocs-ui/components/dialog/search-algolia';
<SearchDialog
defaultTag="value"
tags={[
{
name: 'Tag Name',
value: 'value',
},
]}
/>;
```
### Orama Cloud
关于设置指南,请参见[集成 Orama Cloud](/docs/headless/search/orama-cloud)。
```tsx title="components/search.tsx"
'use client';
import { OramaClient } from '@oramacloud/client';
import type { SharedProps } from 'fumadocs-ui/components/dialog/search';
import SearchDialog from 'fumadocs-ui/components/dialog/search-orama';
const client = new OramaClient({
endpoint: 'endpoint',
api_key: 'apiKey',
});
export default function CustomSearchDialog(props: SharedProps) {
return <SearchDialog {...props} client={client} showOrama />;
}
```
1. 将 `endpoint`、`apiKey` 替换为您想要的值。
2. 用您的新组件[替换默认搜索对话框](#replace-search-dialog)。

View File

@ -1,171 +0,0 @@
---
title: Themes
description: Add Theme to Fumadocs UI
image: /images/blog/post-7.png
date: "2025-01-15"
published: true
categories: [product, news]
author: mkdirs
---
## Usage
Note only Tailwind CSS v4 is supported:
```css title="Tailwind CSS"
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
/* path of `fumadocs-ui` relative to the CSS file */
@source '../node_modules/fumadocs-ui/dist/**/*.js';
```
### Preflight Changes
By using the Tailwind CSS plugin, or the pre-built stylesheet, your default border, text and background
colors will be changed.
### Light/Dark Modes
Fumadocs supports light/dark modes with [`next-themes`](https://github.com/pacocoursey/next-themes), it is included in Root Provider.
See [Root Provider](/docs/layouts/root-provider#theme-provider) to learn more.
### RTL Layout
RTL (Right-to-left) layout is supported.
To enable RTL, set the `dir` prop to `rtl` in body and root provider (required for Radix UI).
```tsx
import { RootProvider } from 'fumadocs-ui/provider';
import type { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body dir="rtl">
<RootProvider dir="rtl">{children}</RootProvider>
</body>
</html>
);
}
```
### Prefix
Fumadocs UI has its own colors, animations, and utilities.
By default, it adds a `fd-` prefix to avoid conflicts with Shadcn UI or your own CSS variables.
You can use them without the prefix by adding some aliases:
```css title="Tailwind CSS"
@theme {
--color-primary: var(--color-fd-primary);
}
```
> You can use it with CSS media queries for responsive design.
### Layout Width
Customise the max width of docs layout with CSS Variables.
```css
:root {
--fd-layout-width: 1400px;
}
```
{/* <WidthTrigger /> */}
## Tailwind CSS Preset
The Tailwind CSS preset introduces new colors and extra utilities including `fd-steps`.
### Themes
It comes with many themes out-of-the-box, you can pick one you prefer.
```css
@import 'fumadocs-ui/css/<theme>.css';
/* Example */
@import 'fumadocs-ui/css/black.css';
```
<Tabs items={['neutral', 'black', 'vitepress', 'dusk', 'catppuccin', 'ocean', 'purple']}>
<Tab value='neutral'>
![Neutral](/images/docs/themes/neutral.png)
</Tab>
<Tab value='black'>
![Black](/images/docs/themes/black.png)
</Tab>
<Tab value='vitepress'>
![Vitepress](/images/docs/themes/vitepress.png)
</Tab>
<Tab value='dusk'>
![Dusk](/images/docs/themes/dusk.png)
</Tab>
<Tab value='Catppuccin'>
![Catppuccin](/images/docs/themes/catppuccin.png)
</Tab>
<Tab value='ocean'>
![Ocean](/images/docs/themes/ocean.png)
</Tab>
<Tab value='purple'>
![Purple](/images/docs/themes/purple.png)
</Tab>
</Tabs>
### Colors
The design system was inspired by [Shadcn UI](https://ui.shadcn.com), you can easily customize the colors using CSS variables.
```css title="global.css"
:root {
--color-fd-background: hsl(0, 0%, 100%);
}
.dark {
--color-fd-background: hsl(0, 0%, 0%);
}
```
### Typography
We have a built-in plugin forked from [Tailwind CSS Typography](https://tailwindcss.com/docs/typography-plugin).
The plugin adds a `prose` class and variants to customise it.
```tsx
<div className="prose">
<h1>Good Heading</h1>
</div>
```
> The plugin works with and only with Fumadocs UI's MDX components, it may conflict with `@tailwindcss/typography`.
> If you need to use `@tailwindcss/typography` over the default plugin, [set a class name option](https://github.com/tailwindlabs/tailwindcss-typography/blob/main/README.md#changing-the-default-class-name) to avoid conflicts.

View File

@ -1,170 +0,0 @@
---
title: 主题
description: 为 Fumadocs UI 添加主题
image: /images/blog/post-7.png
date: "2025-01-15"
published: true
categories: [product, news]
author: mkdirs
---
## 使用方法
注意只支持 Tailwind CSS v4
```css title="Tailwind CSS"
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
/* path of `fumadocs-ui` relative to the CSS file */
@source '../node_modules/fumadocs-ui/dist/**/*.js';
```
### 预设更改
通过使用 Tailwind CSS 插件或预构建的样式表,您的默认边框、文本和背景颜色将被更改。
### 明/暗模式
Fumadocs 通过 [`next-themes`](https://github.com/pacocoursey/next-themes) 支持明/暗模式,它包含在 Root Provider 中。
参见 [Root Provider](/docs/layouts/root-provider#theme-provider) 了解更多信息。
### RTL 布局
支持 RTL从右到左布局。
要启用 RTL请在 body 和 root providerRadix UI 需要)中将 `dir` 属性设置为 `rtl`。
```tsx
import { RootProvider } from 'fumadocs-ui/provider';
import type { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body dir="rtl">
<RootProvider dir="rtl">{children}</RootProvider>
</body>
</html>
);
}
```
### 前缀
Fumadocs UI 有自己的颜色、动画和工具。
默认情况下,它添加了 `fd-` 前缀,以避免与 Shadcn UI 或您自己的 CSS 变量冲突。
您可以通过添加一些别名来使用它们,而无需前缀:
```css title="Tailwind CSS"
@theme {
--color-primary: var(--color-fd-primary);
}
```
> 您可以将其与 CSS 媒体查询一起使用,实现响应式设计。
### 布局宽度
使用 CSS 变量自定义文档布局的最大宽度。
```css
:root {
--fd-layout-width: 1400px;
}
```
{/* <WidthTrigger /> */}
## Tailwind CSS 预设
Tailwind CSS 预设引入了新的颜色和额外的工具,包括 `fd-steps`。
### 主题
它开箱即用地提供了许多主题,您可以选择一个您喜欢的。
```css
@import 'fumadocs-ui/css/<theme>.css';
/* Example */
@import 'fumadocs-ui/css/black.css';
```
<Tabs items={['neutral', 'black', 'vitepress', 'dusk', 'catppuccin', 'ocean', 'purple']}>
<Tab value='neutral'>
![Neutral](/images/docs/themes/neutral.png)
</Tab>
<Tab value='black'>
![Black](/images/docs/themes/black.png)
</Tab>
<Tab value='vitepress'>
![Vitepress](/images/docs/themes/vitepress.png)
</Tab>
<Tab value='dusk'>
![Dusk](/images/docs/themes/dusk.png)
</Tab>
<Tab value='Catppuccin'>
![Catppuccin](/images/docs/themes/catppuccin.png)
</Tab>
<Tab value='ocean'>
![Ocean](/images/docs/themes/ocean.png)
</Tab>
<Tab value='purple'>
![Purple](/images/docs/themes/purple.png)
</Tab>
</Tabs>
### 颜色
设计系统的灵感来自 [Shadcn UI](https://ui.shadcn.com),您可以使用 CSS 变量轻松自定义颜色。
```css title="global.css"
:root {
--color-fd-background: hsl(0, 0%, 100%);
}
.dark {
--color-fd-background: hsl(0, 0%, 0%);
}
```
### 排版
我们有一个内置插件,它是从 [Tailwind CSS Typography](https://tailwindcss.com/docs/typography-plugin) 派生而来的。
该插件添加了一个 `prose` 类和变体来自定义它。
```tsx
<div className="prose">
<h1>Good Heading</h1>
</div>
```
> 该插件仅与 Fumadocs UI 的 MDX 组件一起工作,它可能与 `@tailwindcss/typography` 冲突。
> 如果您需要使用 `@tailwindcss/typography` 而不是默认插件,请[设置类名选项](https://github.com/tailwindlabs/tailwindcss-typography/blob/main/README.md#changing-the-default-class-name)以避免冲突。

View File

@ -1,11 +1,11 @@
---
title: What is Fumadocs
description: Introducing Fumadocs, a docs framework that you can break.
image: /images/blog/post-1.png
date: "2025-04-01"
image: /images/blog/boilerplatehunt-og.png
date: 2024-11-24T12:00:00.000Z
published: true
categories: [company, product]
author: fox
author: mksaas
---
Fumadocs was created because I wanted a more customisable experience for building docs, to be a docs framework that is not opinionated, **a "framework" that you can break**.

View File

@ -1,11 +1,11 @@
---
title: 什么是 Fumadocs
description: 介绍 Fumadocs一个可以打破常规的文档框架
image: /images/blog/post-1.png
date: "2025-04-01"
image: /images/blog/boilerplatehunt-og.png
date: 2024-11-24T12:00:00.000Z
published: true
categories: [company, product]
author: fox
author: mksaas
---
Fumadocs 的创建是因为我想要一种更加可定制化的文档构建体验,一个不固执己见的文档框架,**一个你可以"打破"的"框架"**。
@ -57,4 +57,4 @@ Fumadocs 为 Next.js 提供了额外的工具,包括语法高亮、文档搜
Fumadocs 由 Fuma 和许多贡献者维护,关注代码库的可维护性。
虽然我们不打算提供人们想要的每一项功能,但我们更专注于使基本功能完美且维护良好。
您也可以通过贡献来帮助 Fumadocs 变得更加有用!
您也可以通过贡献来帮助 Fumadocs 变得更加有用!

View File

@ -1,4 +1,5 @@
---
slug: company
name: Company
description: Company news and updates
---

View File

@ -1,4 +1,6 @@
---
slug: company
name: 公司
description: 公司新闻和更新
locale: zh
---

View File

@ -1,4 +1,5 @@
---
slug: news
name: News
description: News and updates about MkSaaS
---

View File

@ -1,4 +1,6 @@
---
slug: news
name: 新闻
description: 最新新闻和更新
locale: zh
---

View File

@ -1,4 +1,5 @@
---
slug: product
name: Product
description: Products and services powered by MkSaaS
---

View File

@ -1,4 +1,6 @@
---
slug: product
name: 产品
description: 产品和服务
locale: zh
---

View File

@ -2,4 +2,4 @@
title: 组件
description: 改进文档的额外组件
index: true
---
---

View File

@ -0,0 +1,45 @@
'use client';
import { buttonVariants } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
import { useEffect, useState } from 'react';
export function UrlBar() {
const [url, setUrl] = useState('');
useEffect(() => {
const timer = setInterval(() => {
setUrl(window.location.pathname + window.location.hash);
}, 100);
return () => {
clearInterval(timer);
};
}, []);
return <pre className="rounded-lg border bg-card p-2 text-sm">{url}</pre>;
}
export function WithoutValueTest() {
const [items, setItems] = useState(['Item 1', 'Item 2']);
return (
<>
<Tabs items={items}>
{items.map((item) => (
<Tab key={item}>{item}</Tab>
))}
</Tabs>
<button
className={cn(
buttonVariants({
variant: 'secondary',
}),
)}
onClick={() => setItems(['Item 1', 'Item 3', 'Item 2'])}
>
Change Items
</button>
</>
);
}

View File

@ -39,7 +39,7 @@ import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
<Tab>Rust is fast</Tab>
</Tabs>
{/* <WithoutValueTest /> */}
<WithoutValueTest />
### Shared Value
@ -119,7 +119,7 @@ to automatically update the URL hash whenever time a new tab is selected:
</Tabs>
```
{/* <UrlBar /> */}
<UrlBar />
<Tabs items={['Hello', 'World']} updateAnchor>
<Tab id="tab-hello" value="Hello">

View File

@ -5,6 +5,15 @@ description: An overview of Fumadocs UI
## Architecture
<UiOverview />
| | |
| ------------- | ------------------------------------------------------- |
| **Sidebar** | Display site title and navigation elements. |
| **Page Tree** | Passed by you, mainly rendered as the items of sidebar. |
| **Docs Page** | All content of the page. |
| **TOC** | Navigation within the article. |
### Page Tree
Navigation elements like sidebar take a [Page Tree](/docs/headless/page-tree) to render navigation links, it's a tree that describes all available pages and folders.
@ -46,6 +55,6 @@ Since the design system is built on Tailwind CSS, you can customise it [with CSS
If none of them suits you, Fumadocs CLI is a tool to install Fumadocs UI components and layouts to your codebase, similar to Shadcn UI. Allowing you to fully customise Fumadocs UI:
```mdx
```package-install
npx fumadocs add
```

View File

@ -5,6 +5,15 @@ description: Fumadocs UI 的概览
## 架构
<UiOverview />
| | |
| ------------- | ------------------------------------------------------- |
| **侧边栏** | 显示站点标题和导航元素。 |
| **页面树** | 由您传递,主要作为侧边栏的项目渲染。 |
| **文档页面** | 页面的所有内容。 |
| **目录** | 文章内导航。 |
### 页面树
侧边栏等导航元素使用[页面树](/docs/headless/page-tree)来渲染导航链接,它是描述所有可用页面和文件夹的树形结构。
@ -46,6 +55,6 @@ Fumadocs UI 还提供了样式化组件,用于交互式示例以增强您的
如果这些都不适合您Fumadocs CLI 是一个工具,可以将 Fumadocs UI 组件和布局安装到您的代码库中,类似于 Shadcn UI。允许您完全自定义 Fumadocs UI
```mdx
```package-install
npx fumadocs add
```
```

View File

@ -95,7 +95,7 @@ title: Hello World
Run the app in development mode and see http://localhost:3000/docs.
```mdx
```package-install
npm run dev
```
@ -244,10 +244,6 @@ will replace the `/docs` page with your `page.tsx`.
</Accordions>
## Video Tutorials
<YoutubeVideo url="https://www.youtube.com/embed/BPnK-YbISHQ?si=TH_tI3e4MCgMHzGr" />
## Learn More
New to here? Don't worry, we are welcome for your questions.

View File

@ -95,7 +95,7 @@ title: Hello World
在开发模式下运行应用程序并查看 http://localhost:3000/docs。
```mdx
```package-install
npm run dev
```
@ -244,10 +244,6 @@ export const source = loader({
</Accordions>
## 视频教程
<YoutubeVideo url="https://www.youtube.com/embed/BPnK-YbISHQ?si=TH_tI3e4MCgMHzGr" />
## 了解更多
刚来这里?别担心,我们欢迎您的问题。
刚来这里?别担心,我们欢迎您的问题。

View File

@ -9,7 +9,7 @@ description: Create a new fumadocs project from scratch.
Create a new Next.js application with `create-next-app`, and install required packages.
```mdx
```package-install
fumadocs-ui fumadocs-core
```
@ -98,9 +98,8 @@ Create a catch-all route `/app/docs/[[...slug]]` for docs pages.
In the page, wrap your content in the [Page](/docs/layouts/page) component.
It may vary depending on your content source. You should configure static rendering with `generateStaticParams` and metadata with `generateMetadata`.
<Tabs items={['Fumadocs MDX', 'Content Collections']}>
<Tabs groupId='content-source' items={['Fumadocs MDX', 'Content Collections']}>
<Tab value='Fumadocs MDX'>
```json doc-gen:file
{
"file": "../../examples/next-mdx/app/docs/[[...slug]]/page.tsx",
@ -109,9 +108,7 @@ It may vary depending on your content source. You should configure static render
}
}
```
</Tab>
<Tab value='Content Collections'>
```json doc-gen:file
{
"file": "../../examples/content-collections/app/docs/[[...slug]]/page.tsx",
@ -120,7 +117,6 @@ It may vary depending on your content source. You should configure static render
}
}
```
</Tab>
</Tabs>
@ -128,9 +124,8 @@ It may vary depending on your content source. You should configure static render
Use the default document search based on Orama.
<Tabs items={['Fumadocs MDX', 'Content Collections']}>
<Tabs groupId='content-source' items={['Fumadocs MDX', 'Content Collections']}>
<Tab value='Fumadocs MDX'>
```json doc-gen:file
{
"file": "../../examples/next-mdx/app/api/search/route.ts",
@ -139,9 +134,7 @@ Use the default document search based on Orama.
}
}
```
</Tab>
<Tab value='Content Collections'>
```json doc-gen:file
{
"file": "../../examples/content-collections/app/api/search/route.ts",
@ -150,7 +143,6 @@ Use the default document search based on Orama.
}
}
```
</Tab>
</Tabs>

View File

@ -9,7 +9,7 @@ description: 从零开始创建一个新的 Fumadocs 项目
使用 `create-next-app` 创建一个新的 Next.js 应用程序,并安装所需的包。
```mdx
```package-install
fumadocs-ui fumadocs-core
```
@ -98,9 +98,8 @@ export default function Layout({ children }: { children: ReactNode }) {
在页面中,将您的内容包装在 [Page](/docs/layouts/page) 组件中。
这可能因您的内容源而异。您应该使用 `generateStaticParams` 配置静态渲染,并使用 `generateMetadata` 配置元数据。
<Tabs items={['Fumadocs MDX', 'Content Collections']}>
<Tabs groupId='content-source' items={['Fumadocs MDX', 'Content Collections']}>
<Tab value='Fumadocs MDX'>
```json doc-gen:file
{
"file": "../../examples/next-mdx/app/docs/[[...slug]]/page.tsx",
@ -109,9 +108,7 @@ export default function Layout({ children }: { children: ReactNode }) {
}
}
```
</Tab>
<Tab value='Content Collections'>
```json doc-gen:file
{
"file": "../../examples/content-collections/app/docs/[[...slug]]/page.tsx",
@ -120,16 +117,15 @@ export default function Layout({ children }: { children: ReactNode }) {
}
}
```
</Tab>
</Tabs>
### 搜索
使用基于 Orama 的默认文档搜索。
<Tabs items={['Fumadocs MDX', 'Content Collections']}>
<Tabs groupId='content-source' items={['Fumadocs MDX', 'Content Collections']}>
<Tab value='Fumadocs MDX'>
```json doc-gen:file
{
"file": "../../examples/next-mdx/app/api/search/route.ts",
@ -138,9 +134,7 @@ export default function Layout({ children }: { children: ReactNode }) {
}
}
```
</Tab>
<Tab value='Content Collections'>
```json doc-gen:file
{
"file": "../../examples/content-collections/app/api/search/route.ts",
@ -149,7 +143,7 @@ export default function Layout({ children }: { children: ReactNode }) {
}
}
```
</Tab>
</Tabs>
了解更多关于[文档搜索](/docs/headless/search)的信息。
@ -188,4 +182,4 @@ WORKDIR /app
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* source.config.ts ./
```
这确保 Fumadocs MDX 在构建期间可以访问您的配置文件。
这确保 Fumadocs MDX 在构建期间可以访问您的配置文件。

View File

@ -293,34 +293,24 @@ You can use code blocks with the `<Tab />` component.
````mdx
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
<Tabs items={['Tab 1', 'Tab 2']}>
<Tab value='Tab 1'>
```ts
console.log('A');
```
</Tab>
<Tab value='Tab 2'>
```ts
console.log('B');
```
</Tab>
</Tabs>
```ts tab="Tab 1"
console.log('A');
```
```ts tab="Tab 2"
console.log('B');
```
````
> Note that you can add MDX components instead of importing them in MDX files.
<Tabs items={['Tab 1', 'Tab 2']}>
<Tab value='Tab 1'>
```ts
console.log('A');
```
</Tab>
<Tab value='Tab 2'>
```ts
console.log('B');
```
</Tab>
</Tabs>
```ts tab="Tab 1"
console.log('A');
```
```ts tab="Tab 2"
console.log('B');
```
### Using Typescript Twoslash
@ -337,8 +327,6 @@ Images are automatically optimized for `next/image`.
![Image](/image.png)
```
![Image](/images/docs/notebook.png)
## Optional
Some optional plugins you can enable.
@ -348,12 +336,12 @@ Some optional plugins you can enable.
Write math equations with TeX.
````md
```mdx
```math
f(x) = x * e^{2 pi i \xi x}
```
````
```mdx
```math
f(x) = x * e^{2 pi i \xi x}
```
@ -364,12 +352,12 @@ To enable, see [Math Integration](/docs/math).
Generate code blocks for installing packages via package managers (JS/Node.js).
````md
```mdx
```package-install
npm i next -D
```
````
```mdx
```package-install
npm i next -D
```

View File

@ -246,97 +246,4 @@ console.log('Hello World');
```
````
### 高亮行
````md
```tsx
<div>Hello World</div> // [\!code highlight]
<div>Hello World</div>
<div>Goodbye</div>
<div>Hello World</div>
```
````
### 高亮单词
您可以通过添加 `[!code word:<match>]` 来高亮特定单词。
````md
```js
// [\!code word:config]
const config = {
reactStrictMode: true,
};
```
````
### 差异
````mdx
```ts
console.log('hewwo'); // [\!code --]
console.log('hello'); // [\!code ++]
```
````
```ts
console.log('hewwo'); // [!code --]
console.log('hello'); // [!code ++]
```
### 标签组
您可以使用 `<Tab />` 组件与代码块一起使用。
````mdx
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
<Tabs items={['Tab 1', 'Tab 2']}>
<Tab value='Tab 1'>
```ts
console.log('A');
```
</Tab>
<Tab value='Tab 2'>
```ts
console.log('B');
```
</Tab>
</Tabs>
````
> 注意,您可以在 MDX 文件中添加 MDX 组件,而不必导入它们。
<Tabs items={['Tab 1', 'Tab 2']}>
<Tab value='Tab 1'>
```ts
console.log('A');
```
</Tab>
<Tab value='Tab 2'>
```ts
console.log('B');
```
</Tab>
</Tabs>
### 使用 Typescript Twoslash
编写带有悬停类型信息和检测到类型错误的 Typescript 代码块。
默认情况下未启用。参见 [Twoslash](/docs/twoslash)。
## 图片
所有内置内容源都能正确处理图片。
图片会自动为 `next/image` 优化。
```mdx
![Image](/image.png)
```
![Image](/images/docs/notebook.png)
## 可选功能
一些您可以启用的可选插件。
### 高亮行

View File

@ -0,0 +1,25 @@
'use client';
import { buttonVariants } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { type ReactElement, useState } from 'react';
export function WidthTrigger(): ReactElement {
const [enabled, setEnabled] = useState(false);
return (
<button
type="button"
className={cn(buttonVariants({ variant: 'secondary' }))}
onClick={() => {
setEnabled((prev) => !prev);
}}
>
{enabled ? <style>{`:root { --fd-layout-width: 1400px; }`}</style> : null}
Trigger Width:
<span className="ms-1.5 text-fd-muted-foreground">
{enabled ? '1400px' : 'default'}
</span>
</button>
);
}

View File

@ -73,7 +73,7 @@ Customise the max width of docs layout with CSS Variables.
}
```
{/* <WidthTrigger /> */}
<WidthTrigger />
## Tailwind CSS Preset

View File

@ -72,7 +72,7 @@ Fumadocs UI 有自己的颜色、动画和工具。
}
```
{/* <WidthTrigger /> */}
<WidthTrigger />
## Tailwind CSS 预设

View File

@ -2,7 +2,6 @@
title: What is Fumadocs
description: Introducing Fumadocs, a docs framework that you can break.
icon: CircleHelp
premium: true
---
Fumadocs was created because I wanted a more customisable experience for building docs, to be a docs framework that is not opinionated, **a "framework" that you can break**.
@ -19,8 +18,6 @@ You are still using features of Next.js App Router, like **Static Site Generatio
**Opinionated on UI:** The only thing Fumadocs UI (the default theme) offers is **User Interface**. The UI is opinionated for bringing better mobile responsiveness and user experience.
Instead, we use a much more flexible approach inspired by Shadcn UI — [Fumadocs CLI](/docs/cli), so we can iterate our design quick, and welcome for more feedback about the UI.
<PremiumContent>
## Why Fumadocs
Fumadocs is designed with flexibility in mind.
@ -59,5 +56,3 @@ docs easier, with less boilerplate.
Fumadocs is maintained by Fuma and many contributors, with care on the maintainability of codebase.
While we don't aim to offer every functionality people wanted, we're more focused on making basic features perfect and well-maintained.
You can also help Fumadocs to be more useful by contributing!
</PremiumContent>

View File

@ -2,7 +2,6 @@
title: 什么是 Fumadocs
description: 介绍 Fumadocs一个可以打破常规的文档框架
icon: CircleHelp
premium: true
---
Fumadocs 的创建是因为我想要一种更加可定制化的文档构建体验,一个不固执己见的文档框架,**一个你可以"打破"的"框架"**。
@ -19,8 +18,6 @@ Fumadocs 的创建是因为我想要一种更加可定制化的文档构建体
**对 UI 有自己的看法:** Fumadocs UI默认主题提供的唯一东西是**用户界面**。UI 的设计理念是提供更好的移动响应性和用户体验。
相反,我们使用受 Shadcn UI 启发的更灵活的方法 — [Fumadocs CLI](/docs/cli),这样我们可以快速迭代设计,并欢迎更多关于 UI 的反馈。
<PremiumContent>
## 为什么选择 Fumadocs
Fumadocs 的设计考虑了灵活性。
@ -56,6 +53,4 @@ Fumadocs 为 Next.js 提供了额外的工具,包括语法高亮、文档搜
Fumadocs 由 Fuma 和许多贡献者维护,关注代码库的可维护性。
虽然我们不打算提供人们想要的每一项功能,但我们更专注于使基本功能完美且维护良好。
您也可以通过贡献来帮助 Fumadocs 变得更加有用!
</PremiumContent>
您也可以通过贡献来帮助 Fumadocs 变得更加有用!

View File

@ -1,8 +1,9 @@
---
title: Cookie Policy
description: How we use cookies and similar technologies on our website
date: "2025-03-10"
date: 2025-03-10T00:00:00.000Z
published: true
locale: en
---
## Introduction
@ -56,4 +57,7 @@ We may update our Cookie Policy from time to time. We will notify you of any cha
## Contact Us
If you have any questions about this Cookie Policy, please [contact us](/contact).
If you have any questions about this Cookie Policy, please contact us:
- By email: support@example.com
- By visiting our website: [Contact Page](https://example.com/contact)

View File

@ -1,8 +1,9 @@
---
title: Cookie 政策
description: 我们如何在网站上使用 Cookie 和类似技术
date: "2025-03-10"
date: 2025-03-10T00:00:00.000Z
published: true
locale: zh
---
## 引言
@ -56,4 +57,7 @@ Cookie 是在您访问网站时存储在您设备上的小型文本文件。它
## 联系我们
如果您对本 Cookie 政策有任何疑问,请[联系我们](/contact)。
如果您对本 Cookie 政策有任何疑问,请联系我们:
- 通过电子邮件support@example.com
- 通过访问我们的网站:[联系页面](https://example.com/contact)

View File

@ -1,8 +1,9 @@
---
title: Privacy Policy
description: Our commitment to protecting your privacy and personal data
date: "2025-03-10"
date: 2025-03-10T00:00:00.000Z
published: true
locale: en
---
## Introduction
@ -42,4 +43,7 @@ We may update our Privacy Policy from time to time. We will notify you of any ch
## Contact Us
If you have any questions about this Privacy Policy, please [contact us](/contact).
If you have any questions about this Privacy Policy, please contact us:
- By email: support@example.com
- By visiting our website: [Contact Page](https://example.com/contact)

View File

@ -1,8 +1,9 @@
---
title: 隐私政策
description: 我们致力于保护您的隐私和个人数据
date: "2025-03-10"
date: 2025-03-10T00:00:00.000Z
published: true
locale: zh
---
## 引言
@ -42,4 +43,7 @@ published: true
## 联系我们
如果您对本隐私政策有任何疑问,请[联系我们](/contact)。
如果您对本隐私政策有任何疑问,请联系我们:
- 通过电子邮件support@example.com
- 通过访问我们的网站:[联系页面](https://example.com/contact)

View File

@ -1,8 +1,9 @@
---
title: Terms of Service
description: The terms and conditions governing the use of our services
date: "2025-03-10"
date: 2025-03-10T00:00:00.000Z
published: true
locale: en
---
## Introduction
@ -48,4 +49,7 @@ We reserve the right to modify these Terms at any time. If we make changes, we w
## Contact Us
If you have any questions about these Terms, please [contact us](/contact).
If you have any questions about these Terms, please contact us:
- By email: support@example.com
- By visiting our website: [Contact Page](https://example.com/contact)

View File

@ -1,8 +1,9 @@
---
title: 服务条款
description: 管理我们服务使用的条款和条件
date: "2025-03-10"
date: 2025-03-10T00:00:00.000Z
published: true
locale: zh
---
## 引言
@ -48,4 +49,7 @@ published: true
## 联系我们
如果您对这些条款有任何疑问,请[联系我们](/contact)。
如果您对这些条款有任何疑问,请联系我们:
- 通过电子邮件support@example.com
- 通过访问我们的网站:[联系页面](https://example.com/contact)

View File

@ -1,7 +1,7 @@
---
title: "Initial Release"
description: "Our first official release with core features and functionality"
date: "2024-03-01"
date: "2024-03-01T00:00:00Z"
version: "v1.0.0"
published: true
---
@ -27,4 +27,4 @@ We're excited to announce the initial release of our platform with the following
- Fixed issues with user registration flow
- Resolved authentication token expiration handling
- Improved form validation and error messages
- Improved form validation and error messages

View File

@ -1,7 +1,7 @@
---
title: "初始版本"
description: "我们的第一个正式版本,包含核心功能"
date: "2024-03-01"
date: "2024-03-01T00:00:00Z"
version: "v1.0.0"
published: true
---
@ -27,4 +27,4 @@ published: true
- 修复了用户注册流程中的问题
- 解决了身份验证令牌过期处理
- 改进了表单验证和错误消息
- 改进了表单验证和错误消息

View File

@ -1,7 +1,7 @@
---
title: "Feature Update"
description: "New features and improvements to enhance your experience"
date: "2024-03-15"
date: "2024-03-15T00:00:00Z"
version: "v1.1.0"
published: true
---
@ -27,4 +27,4 @@ We've added several new features to improve your experience:
- Fixed issue with project duplication
- Resolved calendar sync problems
- Fixed data import validation errors
- Improved error handling for API requests
- Improved error handling for API requests

View File

@ -1,7 +1,7 @@
---
title: "功能更新"
description: "新功能和改进,提升您的使用体验"
date: "2024-03-15"
date: "2024-03-15T00:00:00Z"
version: "v1.1.0"
published: true
---
@ -27,4 +27,4 @@ published: true
- 修复了项目复制问题
- 解决了日历同步问题
- 修复了数据导入验证错误
- 改进了API请求的错误处理
- 改进了API请求的错误处理

View File

@ -1,7 +1,7 @@
---
title: "AI Integration"
description: "Introducing AI-powered features to boost productivity"
date: "2024-03-30"
date: "2024-03-30T00:00:00Z"
version: "v1.2.0"
published: true
---
@ -34,4 +34,4 @@ We're thrilled to introduce our new AI capabilities:
- Fixed issues with file uploads on certain browsers
- Resolved synchronization issues between devices
- Improved error handling for third-party integrations
- Fixed accessibility issues in the dashboard
- Fixed accessibility issues in the dashboard

View File

@ -1,7 +1,7 @@
---
title: "AI集成"
description: "引入AI驱动的功能提高生产力"
date: "2024-03-30"
date: "2024-03-30T00:00:00Z"
version: "v1.2.0"
published: true
---
@ -34,4 +34,4 @@ published: true
- 修复了某些浏览器上文件上传的问题
- 解决了设备之间的同步问题
- 改进了第三方集成的错误处理
- 修复了仪表板中的可访问性问题
- 修复了仪表板中的可访问性问题

View File

@ -1 +0,0 @@
NEXTJS_ENV=development

View File

@ -5,7 +5,7 @@ import { defineConfig } from 'drizzle-kit';
* https://orm.drizzle.team/docs/get-started/neon-new#step-5---setup-drizzle-config-file
*/
export default defineConfig({
out: './src/db/migrations',
out: './drizzle',
schema: './src/db/schema.ts',
dialect: 'postgresql',
dbCredentials: {

View File

@ -14,24 +14,6 @@ CREATE TABLE "account" (
"updated_at" timestamp NOT NULL
);
--> statement-breakpoint
CREATE TABLE "payment" (
"id" text PRIMARY KEY NOT NULL,
"price_id" text NOT NULL,
"type" text NOT NULL,
"interval" text,
"user_id" text NOT NULL,
"customer_id" text NOT NULL,
"subscription_id" text,
"status" text NOT NULL,
"period_start" timestamp,
"period_end" timestamp,
"cancel_at_period_end" boolean,
"trial_start" timestamp,
"trial_end" timestamp,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "session" (
"id" text PRIMARY KEY NOT NULL,
"expires_at" timestamp NOT NULL,
@ -41,7 +23,6 @@ CREATE TABLE "session" (
"ip_address" text,
"user_agent" text,
"user_id" text NOT NULL,
"impersonated_by" text,
CONSTRAINT "session_token_unique" UNIQUE("token")
);
--> statement-breakpoint
@ -53,11 +34,6 @@ CREATE TABLE "user" (
"image" text,
"created_at" timestamp NOT NULL,
"updated_at" timestamp NOT NULL,
"role" text,
"banned" boolean,
"ban_reason" text,
"ban_expires" timestamp,
"customer_id" text,
CONSTRAINT "user_email_unique" UNIQUE("email")
);
--> statement-breakpoint
@ -71,5 +47,4 @@ CREATE TABLE "verification" (
);
--> statement-breakpoint
ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "payment" ADD CONSTRAINT "payment_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;

View File

@ -0,0 +1,5 @@
ALTER TABLE "session" ADD COLUMN "impersonated_by" text;--> statement-breakpoint
ALTER TABLE "user" ADD COLUMN "role" text;--> statement-breakpoint
ALTER TABLE "user" ADD COLUMN "banned" boolean;--> statement-breakpoint
ALTER TABLE "user" ADD COLUMN "ban_reason" text;--> statement-breakpoint
ALTER TABLE "user" ADD COLUMN "ban_expires" timestamp;

View File

@ -0,0 +1,2 @@
ALTER TABLE "user" ADD COLUMN "username" text;--> statement-breakpoint
ALTER TABLE "user" ADD CONSTRAINT "user_username_unique" UNIQUE("username");

View File

@ -0,0 +1,2 @@
ALTER TABLE "user" ADD COLUMN "customer_id" text;--> statement-breakpoint
ALTER TABLE "user" ADD CONSTRAINT "user_customer_id_unique" UNIQUE("customer_id");

View File

@ -0,0 +1,2 @@
ALTER TABLE "user" DROP CONSTRAINT "user_customer_id_unique";--> statement-breakpoint
ALTER TABLE "user" DROP COLUMN "customer_id";

View File

@ -0,0 +1 @@
ALTER TABLE "user" ADD COLUMN "customer_id" text;

Some files were not shown because too many files have changed in this diff Show More