Compare commits
1 Commits
cloudflare
...
dev/matsu-
Author | SHA1 | Date | |
---|---|---|---|
|
151d0d511a |
@ -5,10 +5,6 @@
|
|||||||
"args": [
|
"args": [
|
||||||
"~/.console-ninja/mcp/"
|
"~/.console-ninja/mcp/"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"context7": {
|
|
||||||
"command": "npx",
|
|
||||||
"args": ["-y", "@upstash/context7-mcp@latest"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description: Best practices for using Vercel AI SDK
|
description: Best practices for using Vercel AI SDK
|
||||||
globs: *.tsx,*.ts
|
globs: **/*.{ts,tsx}
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -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
|
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
description: Best practices for date and time manipulation with date-fns
|
description: Best practices for date and time manipulation with date-fns
|
||||||
globs: *.ts,*.tsx
|
globs: **/*.{ts,tsx,js,jsx}
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
|
|
||||||
- Use the `format` function for consistent date formatting across your application.
|
- Use the `format` function for consistent date formatting across your application.
|
||||||
- Implement proper timezone handling using the `utcToZonedTime` function.
|
- Implement proper timezone handling using the `utcToZonedTime` function.
|
||||||
- Utilize the `intervalToDuration` function for calculating time differences.
|
- Utilize the `intervalToDuration` function for calculating time differences.
|
||||||
- Leverage the `isWithinInterval` function for date range checks.
|
- Leverage the `isWithinInterval` function for date range checks.
|
@ -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
|
|
@ -1,7 +1,6 @@
|
|||||||
---
|
---
|
||||||
description: Best practices for using Drizzle ORM with database
|
description: Best practices for using Drizzle ORM with database
|
||||||
globs: *.tsx,*.ts
|
globs: **/*.{ts}
|
||||||
alwaysApply: false
|
|
||||||
---
|
---
|
||||||
|
|
||||||
- Use Drizzle's type-safe query builder for better code completion and safety.
|
- Use Drizzle's type-safe query builder for better code completion and safety.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description: Best practices for Next.js applications and routing
|
description: Best practices for Next.js applications and routing
|
||||||
globs: *.tsx,*.ts
|
globs: **/*.{ts,tsx}
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -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
|
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description: Best practices for using Radix UI components
|
description: Best practices for using Radix UI components
|
||||||
globs: *.tsx,*.ts
|
globs: **/*.{ts,tsx}
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description: Best practices for React component development
|
description: Best practices for React component development
|
||||||
globs: *.tsx,*.ts
|
globs: **/*.{ts,tsx,js,jsx}
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
description: Best practices for form handling with React Hook Form
|
description: Best practices for form handling with React Hook Form
|
||||||
globs: *.tsx,*.ts
|
globs: **/*.{ts,tsx}
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
|
|
||||||
- Use the `useForm` hook for efficient form state management.
|
- Use the `useForm` hook for efficient form state management.
|
||||||
- Implement validation using Zod with `@hookform/resolvers` for type-safe form validation.
|
- Implement validation using Zod with `@hookform/resolvers` for type-safe form validation.
|
||||||
- Utilize the `Controller` component for integrating with custom inputs.
|
- 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.
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description: Best practices for integrating Stripe payments
|
description: Best practices for integrating Stripe payments
|
||||||
globs: *.tsx,*.ts
|
globs: **/*.{ts,tsx}
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description: Best practices for styling with Tailwind CSS
|
description: Best practices for styling with Tailwind CSS
|
||||||
globs: *.tsx,*.ts
|
globs: **/*.{ts,tsx,css}
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
description: TypeScript coding standards and type safety guidelines
|
description: TypeScript coding standards and type safety guidelines
|
||||||
globs: *.tsx,*.ts
|
globs: **/*.{ts,tsx}
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -11,4 +11,4 @@ alwaysApply: false
|
|||||||
- Use strict null checks to prevent null and undefined errors
|
- Use strict null checks to prevent null and undefined errors
|
||||||
- Implement proper type inference using generics for reusable components.
|
- Implement proper type inference using generics for reusable components.
|
||||||
- Utilize type guards and assertions for runtime type checking.
|
- 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.
|
@ -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
|
|
@ -1,7 +1,6 @@
|
|||||||
---
|
---
|
||||||
description: Best practices for schema validation with Zod
|
description: Best practices for schema validation with Zod
|
||||||
globs: *.tsx,*.ts
|
globs: **/*.{ts,tsx}
|
||||||
alwaysApply: false
|
|
||||||
---
|
---
|
||||||
|
|
||||||
- Define clear and reusable schemas for data validation
|
- Define clear and reusable schemas for data validation
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
description: Best practices for state management with Zustand
|
description: Best practices for state management with Zustand
|
||||||
globs: *.tsx,*.ts
|
globs: **/*.{ts,tsx}
|
||||||
alwaysApply: false
|
alwaysApply: false
|
||||||
---
|
---
|
||||||
|
|
||||||
- Use the `create` function to define your store for simplicity and performance.
|
- Use the `create` function to define your store for simplicity and performance.
|
||||||
- Implement middleware like `persist` for persisting state across sessions.
|
- Implement middleware like `persist` for persisting state across sessions.
|
||||||
- Utilize the `useStore` hook for accessing store state in components.
|
- 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
46
.cursorrules
Normal 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
|
@ -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
41
.gitattributes
vendored
@ -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
|
|
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -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.
|
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -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.
|
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -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
25
.gitignore
vendored
@ -30,38 +30,15 @@ yarn-debug.log*
|
|||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
.pnpm-debug.log*
|
.pnpm-debug.log*
|
||||||
|
|
||||||
certificates
|
|
||||||
|
|
||||||
# env files (can opt-in for committing if needed)
|
# env files (can opt-in for committing if needed)
|
||||||
.env*
|
.env*
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
|
||||||
# claude code
|
|
||||||
.claude
|
|
||||||
|
|
||||||
# conductor
|
|
||||||
.conductor
|
|
||||||
|
|
||||||
# kiro
|
|
||||||
.kiro
|
|
||||||
|
|
||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
# content collections
|
# content collections
|
||||||
.content-collections
|
.content-collections
|
||||||
|
|
||||||
# fumadocs
|
|
||||||
.source
|
|
||||||
|
|
||||||
# OpenNext build output
|
|
||||||
.open-next
|
|
||||||
|
|
||||||
# wrangler files
|
|
||||||
.wrangler
|
|
||||||
.dev.vars
|
|
||||||
.dev.vars*
|
|
||||||
!.dev.vars.example
|
|
10
.vscode/extensions.json
vendored
10
.vscode/extensions.json
vendored
@ -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
40
.vscode/settings.json
vendored
@ -1,35 +1,7 @@
|
|||||||
{
|
{
|
||||||
"i18n-ally.localesPaths": [
|
"i18n-ally.localesPaths": [
|
||||||
"messages",
|
"messages",
|
||||||
"src/i18n"
|
"src/i18n"
|
||||||
],
|
],
|
||||||
"i18n-ally.keystyle": "nested",
|
"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
|
|
||||||
}
|
|
||||||
}
|
|
109
CLAUDE.md
109
CLAUDE.md
@ -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
|
|
62
Dockerfile
62
Dockerfile
@ -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
1
ISSUES.md
Normal 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
41
LICENSE
@ -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 MkSaaS’s 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 MkSaaS’s 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.
|
|
56
README.md
56
README.md
@ -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)
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||||
- 🌐 demo: [demo.mksaas.com](https://demo.mksaas.com)
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||||
- 📚 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)
|
|
||||||
|
|
||||||
## 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 you’re unable to access any of them, please don’t hesitate to reach out to me, and I’ll assist you in resolving the issue.
|
## Deploy on Vercel
|
||||||
|
|
||||||
- [mksaas-template (ready)](https://github.com/MkSaaSHQ/mksaas-template): https://demo.mksaas.com
|
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.
|
||||||
- [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
|
|
||||||
|
|
||||||
## Notice
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||||
|
|
||||||
> 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.
|
|
||||||
|
61
biome.json
61
biome.json
@ -9,28 +9,14 @@
|
|||||||
"ignoreUnknown": true,
|
"ignoreUnknown": true,
|
||||||
"ignore": [
|
"ignore": [
|
||||||
".next/**",
|
".next/**",
|
||||||
".open-next/**",
|
".content-collections/**",
|
||||||
".wrangler/**",
|
|
||||||
".cursor/**",
|
|
||||||
".claude/**",
|
|
||||||
".kiro/**",
|
|
||||||
".conductor/**",
|
|
||||||
".vscode/**",
|
|
||||||
".source/**",
|
|
||||||
"node_modules/**",
|
"node_modules/**",
|
||||||
"dist/**",
|
"dist/**",
|
||||||
"build/**",
|
"build/**",
|
||||||
"src/db/**",
|
|
||||||
"tailwind.config.ts",
|
|
||||||
"src/components/ui/*.tsx",
|
"src/components/ui/*.tsx",
|
||||||
"src/components/magicui/*.tsx",
|
"src/components/magicui/*.tsx",
|
||||||
"src/components/animate-ui/*.tsx",
|
"tailwind.config.ts",
|
||||||
"src/components/tailark/*.tsx",
|
"src/db/schema.ts"
|
||||||
"src/components/ai-elements/*.tsx",
|
|
||||||
"src/app/[[]locale]/preview/**",
|
|
||||||
"src/payment/types.ts",
|
|
||||||
"src/credits/types.ts",
|
|
||||||
"src/types/index.d.ts"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"formatter": {
|
"formatter": {
|
||||||
@ -38,8 +24,7 @@
|
|||||||
"indentStyle": "space",
|
"indentStyle": "space",
|
||||||
"indentWidth": 2,
|
"indentWidth": 2,
|
||||||
"lineWidth": 80,
|
"lineWidth": 80,
|
||||||
"formatWithErrors": true,
|
"formatWithErrors": true
|
||||||
"useEditorconfig": true
|
|
||||||
},
|
},
|
||||||
"organizeImports": {
|
"organizeImports": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
@ -49,53 +34,27 @@
|
|||||||
"rules": {
|
"rules": {
|
||||||
"recommended": true,
|
"recommended": true,
|
||||||
"suspicious": {
|
"suspicious": {
|
||||||
"noSparseArray": "off",
|
"noSparseArray": "off"
|
||||||
"noArrayIndexKey": "off",
|
|
||||||
"noExplicitAny": "off",
|
|
||||||
"noShadowRestrictedNames": "off"
|
|
||||||
},
|
|
||||||
"complexity": {
|
|
||||||
"noForEach": "off"
|
|
||||||
},
|
|
||||||
"correctness": {
|
|
||||||
"useExhaustiveDependencies": "off"
|
|
||||||
},
|
},
|
||||||
"style": {
|
"style": {
|
||||||
"useTemplate": "off",
|
"useTemplate": "off",
|
||||||
"noNonNullAssertion": "off",
|
"noNonNullAssertion": "off",
|
||||||
"useShorthandArrayType": "off",
|
"useShorthandArrayType": "off"
|
||||||
"useNodejsImportProtocol": "off"
|
|
||||||
},
|
},
|
||||||
"a11y": {
|
"a11y": {
|
||||||
"useValidAnchor": "off",
|
"useValidAnchor": "off"
|
||||||
"noSvgWithoutTitle": "off",
|
|
||||||
"useKeyWithClickEvents": "off"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ignore": [
|
"ignore": [
|
||||||
".next/**",
|
".next/**",
|
||||||
".open-next/**",
|
".content-collections/**",
|
||||||
".wrangler/**",
|
|
||||||
".cursor/**",
|
|
||||||
".claude/**",
|
|
||||||
".conductor/**",
|
|
||||||
".kiro/**",
|
|
||||||
".vscode/**",
|
|
||||||
".source/**",
|
|
||||||
"node_modules/**",
|
"node_modules/**",
|
||||||
"dist/**",
|
"dist/**",
|
||||||
"build/**",
|
"build/**",
|
||||||
"src/db/**",
|
|
||||||
"tailwind.config.ts",
|
|
||||||
"src/components/ui/*.tsx",
|
"src/components/ui/*.tsx",
|
||||||
"src/components/magicui/*.tsx",
|
"src/components/magicui/*.tsx",
|
||||||
"src/components/animate-ui/*.tsx",
|
"tailwind.config.ts",
|
||||||
"src/components/tailark/*.tsx",
|
"src/db/schema.ts"
|
||||||
"src/components/ai-elements/*.tsx",
|
|
||||||
"src/app/[[]locale]/preview/**",
|
|
||||||
"src/payment/types.ts",
|
|
||||||
"src/credits/types.ts",
|
|
||||||
"src/types/index.d.ts"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"javascript": {
|
"javascript": {
|
||||||
|
7483
cloudflare-env.d.ts
vendored
7483
cloudflare-env.d.ts
vendored
File diff suppressed because it is too large
Load Diff
340
content-collections.ts
Normal file
340
content-collections.ts
Normal 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]
|
||||||
|
});
|
@ -1,4 +0,0 @@
|
|||||||
---
|
|
||||||
name: Fox
|
|
||||||
avatar: /images/avatars/fox.png
|
|
||||||
---
|
|
@ -1,4 +0,0 @@
|
|||||||
---
|
|
||||||
name: Fox
|
|
||||||
avatar: /images/avatars/fox.png
|
|
||||||
---
|
|
@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
slug: mkdirs
|
||||||
name: Mkdirs
|
name: Mkdirs
|
||||||
avatar: /images/avatars/mkdirs.png
|
avatar: /images/avatars/mkdirs.png
|
||||||
---
|
---
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
name: Mkdirs模板
|
slug: mkdirs
|
||||||
|
name: Mkdirs
|
||||||
avatar: /images/avatars/mkdirs.png
|
avatar: /images/avatars/mkdirs.png
|
||||||
---
|
---
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
slug: mksaas
|
||||||
name: MkSaaS
|
name: MkSaaS
|
||||||
avatar: /images/avatars/mksaas.png
|
avatar: /images/avatars/mksaas.png
|
||||||
---
|
---
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
name: MkSaaS模板
|
slug: mksaas
|
||||||
|
name: MkSaaS
|
||||||
avatar: /images/avatars/mksaas.png
|
avatar: /images/avatars/mksaas.png
|
||||||
---
|
---
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: Comparisons
|
title: Comparisons
|
||||||
description: How is Fumadocs different from other existing frameworks?
|
description: How is Fumadocs different from other existing frameworks?
|
||||||
image: /images/blog/post-2.png
|
image: /images/blog/mkdirs-og.png
|
||||||
date: "2025-03-22"
|
date: 2024-11-25T12:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
categories: [news, company]
|
categories: [news, company]
|
||||||
author: fox
|
author: mkdirs
|
||||||
---
|
---
|
||||||
|
|
||||||
## Nextra
|
## Nextra
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: 对比
|
title: 对比
|
||||||
description: Fumadocs 与其他现有框架有何不同?
|
description: Fumadocs 与其他现有框架有何不同?
|
||||||
image: /images/blog/post-2.png
|
image: /images/blog/mkdirs-og.png
|
||||||
date: "2025-03-22"
|
date: 2024-11-25T12:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
categories: [news, company]
|
categories: [news, company]
|
||||||
author: fox
|
author: mkdirs
|
||||||
---
|
---
|
||||||
|
|
||||||
## Nextra
|
## Nextra
|
||||||
@ -69,4 +69,4 @@ Docusaurus 是一个基于 React.js 的强大框架。它通过插件和自定
|
|||||||
|
|
||||||
您可以通过插件轻松实现许多功能,他们的生态系统确实更大,并由许多贡献者维护。
|
您可以通过插件轻松实现许多功能,他们的生态系统确实更大,并由许多贡献者维护。
|
||||||
|
|
||||||
相比之下,Fumadocs 的灵活性允许您自己实现它们,可能需要更长的时间来调整它以达到您的满意度。
|
相比之下,Fumadocs 的灵活性允许您自己实现它们,可能需要更长的时间来调整它以达到您的满意度。
|
@ -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>
|
|
@ -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 和 MDX(Markdown 的超集)。
|
|
||||||
|
|
||||||
虽然不是必需的,但对 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>
|
|
||||||
|
|
||||||
## 了解更多
|
|
||||||
|
|
||||||
刚来这里?别担心,我们欢迎您的问题。
|
|
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: Internationalization
|
title: Internationalization
|
||||||
description: Support multiple languages in your documentation
|
description: Support multiple languages in your documentation
|
||||||
image: /images/blog/post-3.png
|
image: /images/blog/mksaas-og.png
|
||||||
date: "2025-03-15"
|
date: 2024-11-26T12:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
categories: [company, product]
|
categories: [company, product]
|
||||||
author: mksaas
|
author: mksaas
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: 国际化
|
title: 国际化
|
||||||
description: 在您的文档中支持多种语言
|
description: 在您的文档中支持多种语言
|
||||||
image: /images/blog/post-3.png
|
image: /images/blog/mksaas-og.png
|
||||||
date: "2025-03-15"
|
date: 2024-11-26T12:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
categories: [company, product]
|
categories: [company, product]
|
||||||
author: mksaas
|
author: mksaas
|
||||||
@ -224,4 +224,4 @@ return <Link href={`/${lang}/another-page`}>This is a link</Link>;
|
|||||||
import { DynamicLink } from 'fumadocs-core/dynamic-link';
|
import { DynamicLink } from 'fumadocs-core/dynamic-link';
|
||||||
|
|
||||||
<DynamicLink href="/[lang]/another-page">This is a link</DynamicLink>
|
<DynamicLink href="/[lang]/another-page">This is a link</DynamicLink>
|
||||||
```
|
```
|
@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: Manual Installation
|
title: Manual Installation
|
||||||
description: Create a new fumadocs project from scratch.
|
description: Create a new fumadocs project from scratch.
|
||||||
image: /images/blog/post-4.png
|
image: /images/blog/mksaas-og.png
|
||||||
date: "2025-03-14"
|
date: 2025-03-31T12:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
categories: [company, product]
|
categories: [company, product]
|
||||||
author: mkdirs
|
author: mksaas
|
||||||
---
|
---
|
||||||
|
|
||||||
> Read the [Quick Start](/docs) guide first for basic concept.
|
> 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.
|
Create a new Next.js application with `create-next-app`, and install required packages.
|
||||||
|
|
||||||
```mdx
|
```package-install
|
||||||
fumadocs-ui fumadocs-core
|
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.
|
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`.
|
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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/next-mdx/app/docs/[[...slug]]/page.tsx",
|
"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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/content-collections/app/docs/[[...slug]]/page.tsx",
|
"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>
|
</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.
|
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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/next-mdx/app/api/search/route.ts",
|
"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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/content-collections/app/api/search/route.ts",
|
"file": "../../examples/content-collections/app/api/search/route.ts",
|
||||||
@ -155,7 +148,6 @@ Use the default document search based on Orama.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
</Tab>
|
|
||||||
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: 手动安装
|
title: 手动安装
|
||||||
description: 从零开始创建一个新的 Fumadocs 项目
|
description: 从零开始创建一个新的 Fumadocs 项目
|
||||||
image: /images/blog/post-4.png
|
image: /images/blog/mksaas-og.png
|
||||||
date: "2025-03-14"
|
date: 2025-03-31T12:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
categories: [company, product]
|
categories: [company, product]
|
||||||
author: mkdirs
|
author: mksaas
|
||||||
---
|
---
|
||||||
|
|
||||||
> 请先阅读[快速入门](/docs)指南了解基本概念。
|
> 请先阅读[快速入门](/docs)指南了解基本概念。
|
||||||
@ -14,7 +14,7 @@ author: mkdirs
|
|||||||
|
|
||||||
使用 `create-next-app` 创建一个新的 Next.js 应用程序,并安装所需的包。
|
使用 `create-next-app` 创建一个新的 Next.js 应用程序,并安装所需的包。
|
||||||
|
|
||||||
```mdx
|
```package-install
|
||||||
fumadocs-ui fumadocs-core
|
fumadocs-ui fumadocs-core
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -103,9 +103,8 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|||||||
在页面中,将您的内容包装在 [Page](/docs/layouts/page) 组件中。
|
在页面中,将您的内容包装在 [Page](/docs/layouts/page) 组件中。
|
||||||
这可能因您的内容源而异。您应该使用 `generateStaticParams` 配置静态渲染,并使用 `generateMetadata` 配置元数据。
|
这可能因您的内容源而异。您应该使用 `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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/next-mdx/app/docs/[[...slug]]/page.tsx",
|
"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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/content-collections/app/docs/[[...slug]]/page.tsx",
|
"file": "../../examples/content-collections/app/docs/[[...slug]]/page.tsx",
|
||||||
@ -125,16 +122,15 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
### 搜索
|
### 搜索
|
||||||
|
|
||||||
使用基于 Orama 的默认文档搜索。
|
使用基于 Orama 的默认文档搜索。
|
||||||
|
|
||||||
<Tabs items={['Fumadocs MDX', 'Content Collections']}>
|
<Tabs groupId='content-source' items={['Fumadocs MDX', 'Content Collections']}>
|
||||||
|
|
||||||
<Tab value='Fumadocs MDX'>
|
|
||||||
```json doc-gen:file
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/next-mdx/app/api/search/route.ts",
|
"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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/content-collections/app/api/search/route.ts",
|
"file": "../../examples/content-collections/app/api/search/route.ts",
|
||||||
@ -154,7 +148,7 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
了解更多关于[文档搜索](/docs/headless/search)的信息。
|
了解更多关于[文档搜索](/docs/headless/search)的信息。
|
||||||
@ -193,4 +187,4 @@ WORKDIR /app
|
|||||||
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* source.config.ts ./
|
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* source.config.ts ./
|
||||||
```
|
```
|
||||||
|
|
||||||
这确保 Fumadocs MDX 在构建期间可以访问您的配置文件。
|
这确保 Fumadocs MDX 在构建期间可以访问您的配置文件。
|
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: Markdown
|
title: Markdown
|
||||||
description: How to write documents
|
description: How to write documents
|
||||||
image: /images/blog/post-5.png
|
image: /images/blog/mkdirs-og.png
|
||||||
date: "2025-03-05"
|
date: 2024-11-25T12:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
categories: [news, company]
|
categories: [news, company]
|
||||||
author: mkdirs
|
author: mkdirs
|
||||||
@ -298,34 +298,24 @@ You can use code blocks with the `<Tab />` component.
|
|||||||
````mdx
|
````mdx
|
||||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||||
|
|
||||||
<Tabs items={['Tab 1', 'Tab 2']}>
|
```ts tab="Tab 1"
|
||||||
<Tab value='Tab 1'>
|
console.log('A');
|
||||||
```ts
|
```
|
||||||
console.log('A');
|
|
||||||
```
|
```ts tab="Tab 2"
|
||||||
</Tab>
|
console.log('B');
|
||||||
<Tab value='Tab 2'>
|
```
|
||||||
```ts
|
|
||||||
console.log('B');
|
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
````
|
````
|
||||||
|
|
||||||
> Note that you can add MDX components instead of importing them in MDX files.
|
> Note that you can add MDX components instead of importing them in MDX files.
|
||||||
|
|
||||||
<Tabs items={['Tab 1', 'Tab 2']}>
|
```ts tab="Tab 1"
|
||||||
<Tab value='Tab 1'>
|
console.log('A');
|
||||||
```ts
|
```
|
||||||
console.log('A');
|
|
||||||
```
|
```ts tab="Tab 2"
|
||||||
</Tab>
|
console.log('B');
|
||||||
<Tab value='Tab 2'>
|
```
|
||||||
```ts
|
|
||||||
console.log('B');
|
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
### Using Typescript Twoslash
|
### Using Typescript Twoslash
|
||||||
|
|
||||||
@ -342,8 +332,6 @@ Images are automatically optimized for `next/image`.
|
|||||||

|

|
||||||
```
|
```
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Optional
|
## Optional
|
||||||
|
|
||||||
Some optional plugins you can enable.
|
Some optional plugins you can enable.
|
||||||
@ -353,12 +341,12 @@ Some optional plugins you can enable.
|
|||||||
Write math equations with TeX.
|
Write math equations with TeX.
|
||||||
|
|
||||||
````md
|
````md
|
||||||
```mdx
|
```math
|
||||||
f(x) = x * e^{2 pi i \xi x}
|
f(x) = x * e^{2 pi i \xi x}
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
```mdx
|
```math
|
||||||
f(x) = x * e^{2 pi i \xi x}
|
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).
|
Generate code blocks for installing packages via package managers (JS/Node.js).
|
||||||
|
|
||||||
````md
|
````md
|
||||||
```mdx
|
```package-install
|
||||||
npm i next -D
|
npm i next -D
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
```mdx
|
```package-install
|
||||||
npm i next -D
|
npm i next -D
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: Markdown
|
title: Markdown
|
||||||
description: 如何撰写文档
|
description: 如何撰写文档
|
||||||
image: /images/blog/post-5.png
|
image: /images/blog/mkdirs-og.png
|
||||||
date: "2025-03-05"
|
date: 2024-11-25T12:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
categories: [news, company]
|
categories: [news, company]
|
||||||
author: mkdirs
|
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
|
|
||||||

|
|
||||||
```
|
|
||||||
|
|
||||||
## 可选功能
|
|
||||||
|
|
||||||
一些您可以启用的可选插件。
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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)。
|
|
@ -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'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab value='black'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab value='vitepress'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab value='dusk'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab value='Catppuccin'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab value='ocean'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab value='purple'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</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.
|
|
@ -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 provider(Radix 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'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab value='black'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab value='vitepress'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab value='dusk'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab value='Catppuccin'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab value='ocean'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Tab>
|
|
||||||
|
|
||||||
<Tab value='purple'>
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</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)以避免冲突。
|
|
@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: What is Fumadocs
|
title: What is Fumadocs
|
||||||
description: Introducing Fumadocs, a docs framework that you can break.
|
description: Introducing Fumadocs, a docs framework that you can break.
|
||||||
image: /images/blog/post-1.png
|
image: /images/blog/boilerplatehunt-og.png
|
||||||
date: "2025-04-01"
|
date: 2024-11-24T12:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
categories: [company, product]
|
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**.
|
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**.
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: 什么是 Fumadocs
|
title: 什么是 Fumadocs
|
||||||
description: 介绍 Fumadocs,一个可以打破常规的文档框架
|
description: 介绍 Fumadocs,一个可以打破常规的文档框架
|
||||||
image: /images/blog/post-1.png
|
image: /images/blog/boilerplatehunt-og.png
|
||||||
date: "2025-04-01"
|
date: 2024-11-24T12:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
categories: [company, product]
|
categories: [company, product]
|
||||||
author: fox
|
author: mksaas
|
||||||
---
|
---
|
||||||
|
|
||||||
Fumadocs 的创建是因为我想要一种更加可定制化的文档构建体验,一个不固执己见的文档框架,**一个你可以"打破"的"框架"**。
|
Fumadocs 的创建是因为我想要一种更加可定制化的文档构建体验,一个不固执己见的文档框架,**一个你可以"打破"的"框架"**。
|
||||||
@ -57,4 +57,4 @@ Fumadocs 为 Next.js 提供了额外的工具,包括语法高亮、文档搜
|
|||||||
|
|
||||||
Fumadocs 由 Fuma 和许多贡献者维护,关注代码库的可维护性。
|
Fumadocs 由 Fuma 和许多贡献者维护,关注代码库的可维护性。
|
||||||
虽然我们不打算提供人们想要的每一项功能,但我们更专注于使基本功能完美且维护良好。
|
虽然我们不打算提供人们想要的每一项功能,但我们更专注于使基本功能完美且维护良好。
|
||||||
您也可以通过贡献来帮助 Fumadocs 变得更加有用!
|
您也可以通过贡献来帮助 Fumadocs 变得更加有用!
|
@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
slug: company
|
||||||
name: Company
|
name: Company
|
||||||
description: Company news and updates
|
description: Company news and updates
|
||||||
---
|
---
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
---
|
---
|
||||||
|
slug: company
|
||||||
name: 公司
|
name: 公司
|
||||||
description: 公司新闻和更新
|
description: 公司新闻和更新
|
||||||
|
locale: zh
|
||||||
---
|
---
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
slug: news
|
||||||
name: News
|
name: News
|
||||||
description: News and updates about MkSaaS
|
description: News and updates about MkSaaS
|
||||||
---
|
---
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
---
|
---
|
||||||
|
slug: news
|
||||||
name: 新闻
|
name: 新闻
|
||||||
description: 最新新闻和更新
|
description: 最新新闻和更新
|
||||||
|
locale: zh
|
||||||
---
|
---
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
slug: product
|
||||||
name: Product
|
name: Product
|
||||||
description: Products and services powered by MkSaaS
|
description: Products and services powered by MkSaaS
|
||||||
---
|
---
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
---
|
---
|
||||||
|
slug: product
|
||||||
name: 产品
|
name: 产品
|
||||||
description: 产品和服务
|
description: 产品和服务
|
||||||
|
locale: zh
|
||||||
---
|
---
|
||||||
|
@ -2,4 +2,4 @@
|
|||||||
title: 组件
|
title: 组件
|
||||||
description: 改进文档的额外组件
|
description: 改进文档的额外组件
|
||||||
index: true
|
index: true
|
||||||
---
|
---
|
45
content/docs/components/tabs.client.tsx
Normal file
45
content/docs/components/tabs.client.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -39,7 +39,7 @@ import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
|||||||
<Tab>Rust is fast</Tab>
|
<Tab>Rust is fast</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
{/* <WithoutValueTest /> */}
|
<WithoutValueTest />
|
||||||
|
|
||||||
### Shared Value
|
### Shared Value
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ to automatically update the URL hash whenever time a new tab is selected:
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
```
|
```
|
||||||
|
|
||||||
{/* <UrlBar /> */}
|
<UrlBar />
|
||||||
|
|
||||||
<Tabs items={['Hello', 'World']} updateAnchor>
|
<Tabs items={['Hello', 'World']} updateAnchor>
|
||||||
<Tab id="tab-hello" value="Hello">
|
<Tab id="tab-hello" value="Hello">
|
||||||
|
@ -5,6 +5,15 @@ description: An overview of Fumadocs UI
|
|||||||
|
|
||||||
## Architecture
|
## 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
|
### 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.
|
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:
|
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
|
npx fumadocs add
|
||||||
```
|
```
|
||||||
|
@ -5,6 +5,15 @@ description: Fumadocs UI 的概览
|
|||||||
|
|
||||||
## 架构
|
## 架构
|
||||||
|
|
||||||
|
<UiOverview />
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| ------------- | ------------------------------------------------------- |
|
||||||
|
| **侧边栏** | 显示站点标题和导航元素。 |
|
||||||
|
| **页面树** | 由您传递,主要作为侧边栏的项目渲染。 |
|
||||||
|
| **文档页面** | 页面的所有内容。 |
|
||||||
|
| **目录** | 文章内导航。 |
|
||||||
|
|
||||||
### 页面树
|
### 页面树
|
||||||
|
|
||||||
侧边栏等导航元素使用[页面树](/docs/headless/page-tree)来渲染导航链接,它是描述所有可用页面和文件夹的树形结构。
|
侧边栏等导航元素使用[页面树](/docs/headless/page-tree)来渲染导航链接,它是描述所有可用页面和文件夹的树形结构。
|
||||||
@ -46,6 +55,6 @@ Fumadocs UI 还提供了样式化组件,用于交互式示例以增强您的
|
|||||||
|
|
||||||
如果这些都不适合您,Fumadocs CLI 是一个工具,可以将 Fumadocs UI 组件和布局安装到您的代码库中,类似于 Shadcn UI。允许您完全自定义 Fumadocs UI:
|
如果这些都不适合您,Fumadocs CLI 是一个工具,可以将 Fumadocs UI 组件和布局安装到您的代码库中,类似于 Shadcn UI。允许您完全自定义 Fumadocs UI:
|
||||||
|
|
||||||
```mdx
|
```package-install
|
||||||
npx fumadocs add
|
npx fumadocs add
|
||||||
```
|
```
|
@ -95,7 +95,7 @@ title: Hello World
|
|||||||
|
|
||||||
Run the app in development mode and see http://localhost:3000/docs.
|
Run the app in development mode and see http://localhost:3000/docs.
|
||||||
|
|
||||||
```mdx
|
```package-install
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -244,10 +244,6 @@ will replace the `/docs` page with your `page.tsx`.
|
|||||||
|
|
||||||
</Accordions>
|
</Accordions>
|
||||||
|
|
||||||
## Video Tutorials
|
|
||||||
|
|
||||||
<YoutubeVideo url="https://www.youtube.com/embed/BPnK-YbISHQ?si=TH_tI3e4MCgMHzGr" />
|
|
||||||
|
|
||||||
## Learn More
|
## Learn More
|
||||||
|
|
||||||
New to here? Don't worry, we are welcome for your questions.
|
New to here? Don't worry, we are welcome for your questions.
|
||||||
|
@ -95,7 +95,7 @@ title: Hello World
|
|||||||
|
|
||||||
在开发模式下运行应用程序并查看 http://localhost:3000/docs。
|
在开发模式下运行应用程序并查看 http://localhost:3000/docs。
|
||||||
|
|
||||||
```mdx
|
```package-install
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -244,10 +244,6 @@ export const source = loader({
|
|||||||
|
|
||||||
</Accordions>
|
</Accordions>
|
||||||
|
|
||||||
## 视频教程
|
|
||||||
|
|
||||||
<YoutubeVideo url="https://www.youtube.com/embed/BPnK-YbISHQ?si=TH_tI3e4MCgMHzGr" />
|
|
||||||
|
|
||||||
## 了解更多
|
## 了解更多
|
||||||
|
|
||||||
刚来这里?别担心,我们欢迎您的问题。
|
刚来这里?别担心,我们欢迎您的问题。
|
@ -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.
|
Create a new Next.js application with `create-next-app`, and install required packages.
|
||||||
|
|
||||||
```mdx
|
```package-install
|
||||||
fumadocs-ui fumadocs-core
|
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.
|
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`.
|
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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/next-mdx/app/docs/[[...slug]]/page.tsx",
|
"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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/content-collections/app/docs/[[...slug]]/page.tsx",
|
"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>
|
</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.
|
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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/next-mdx/app/api/search/route.ts",
|
"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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/content-collections/app/api/search/route.ts",
|
"file": "../../examples/content-collections/app/api/search/route.ts",
|
||||||
@ -150,7 +143,6 @@ Use the default document search based on Orama.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
</Tab>
|
|
||||||
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ description: 从零开始创建一个新的 Fumadocs 项目
|
|||||||
|
|
||||||
使用 `create-next-app` 创建一个新的 Next.js 应用程序,并安装所需的包。
|
使用 `create-next-app` 创建一个新的 Next.js 应用程序,并安装所需的包。
|
||||||
|
|
||||||
```mdx
|
```package-install
|
||||||
fumadocs-ui fumadocs-core
|
fumadocs-ui fumadocs-core
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -98,9 +98,8 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|||||||
在页面中,将您的内容包装在 [Page](/docs/layouts/page) 组件中。
|
在页面中,将您的内容包装在 [Page](/docs/layouts/page) 组件中。
|
||||||
这可能因您的内容源而异。您应该使用 `generateStaticParams` 配置静态渲染,并使用 `generateMetadata` 配置元数据。
|
这可能因您的内容源而异。您应该使用 `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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/next-mdx/app/docs/[[...slug]]/page.tsx",
|
"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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/content-collections/app/docs/[[...slug]]/page.tsx",
|
"file": "../../examples/content-collections/app/docs/[[...slug]]/page.tsx",
|
||||||
@ -120,16 +117,15 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
### 搜索
|
### 搜索
|
||||||
|
|
||||||
使用基于 Orama 的默认文档搜索。
|
使用基于 Orama 的默认文档搜索。
|
||||||
|
|
||||||
<Tabs items={['Fumadocs MDX', 'Content Collections']}>
|
<Tabs groupId='content-source' items={['Fumadocs MDX', 'Content Collections']}>
|
||||||
|
|
||||||
<Tab value='Fumadocs MDX'>
|
|
||||||
```json doc-gen:file
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/next-mdx/app/api/search/route.ts",
|
"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
|
```json doc-gen:file
|
||||||
{
|
{
|
||||||
"file": "../../examples/content-collections/app/api/search/route.ts",
|
"file": "../../examples/content-collections/app/api/search/route.ts",
|
||||||
@ -149,7 +143,7 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
了解更多关于[文档搜索](/docs/headless/search)的信息。
|
了解更多关于[文档搜索](/docs/headless/search)的信息。
|
||||||
@ -188,4 +182,4 @@ WORKDIR /app
|
|||||||
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* source.config.ts ./
|
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* source.config.ts ./
|
||||||
```
|
```
|
||||||
|
|
||||||
这确保 Fumadocs MDX 在构建期间可以访问您的配置文件。
|
这确保 Fumadocs MDX 在构建期间可以访问您的配置文件。
|
@ -293,34 +293,24 @@ You can use code blocks with the `<Tab />` component.
|
|||||||
````mdx
|
````mdx
|
||||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||||
|
|
||||||
<Tabs items={['Tab 1', 'Tab 2']}>
|
```ts tab="Tab 1"
|
||||||
<Tab value='Tab 1'>
|
console.log('A');
|
||||||
```ts
|
```
|
||||||
console.log('A');
|
|
||||||
```
|
```ts tab="Tab 2"
|
||||||
</Tab>
|
console.log('B');
|
||||||
<Tab value='Tab 2'>
|
```
|
||||||
```ts
|
|
||||||
console.log('B');
|
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
````
|
````
|
||||||
|
|
||||||
> Note that you can add MDX components instead of importing them in MDX files.
|
> Note that you can add MDX components instead of importing them in MDX files.
|
||||||
|
|
||||||
<Tabs items={['Tab 1', 'Tab 2']}>
|
```ts tab="Tab 1"
|
||||||
<Tab value='Tab 1'>
|
console.log('A');
|
||||||
```ts
|
```
|
||||||
console.log('A');
|
|
||||||
```
|
```ts tab="Tab 2"
|
||||||
</Tab>
|
console.log('B');
|
||||||
<Tab value='Tab 2'>
|
```
|
||||||
```ts
|
|
||||||
console.log('B');
|
|
||||||
```
|
|
||||||
</Tab>
|
|
||||||
</Tabs>
|
|
||||||
|
|
||||||
### Using Typescript Twoslash
|
### Using Typescript Twoslash
|
||||||
|
|
||||||
@ -337,8 +327,6 @@ Images are automatically optimized for `next/image`.
|
|||||||

|

|
||||||
```
|
```
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Optional
|
## Optional
|
||||||
|
|
||||||
Some optional plugins you can enable.
|
Some optional plugins you can enable.
|
||||||
@ -348,12 +336,12 @@ Some optional plugins you can enable.
|
|||||||
Write math equations with TeX.
|
Write math equations with TeX.
|
||||||
|
|
||||||
````md
|
````md
|
||||||
```mdx
|
```math
|
||||||
f(x) = x * e^{2 pi i \xi x}
|
f(x) = x * e^{2 pi i \xi x}
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
```mdx
|
```math
|
||||||
f(x) = x * e^{2 pi i \xi x}
|
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).
|
Generate code blocks for installing packages via package managers (JS/Node.js).
|
||||||
|
|
||||||
````md
|
````md
|
||||||
```mdx
|
```package-install
|
||||||
npm i next -D
|
npm i next -D
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
```mdx
|
```package-install
|
||||||
npm i next -D
|
npm i next -D
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -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
|
|
||||||

|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## 可选功能
|
|
||||||
|
|
||||||
一些您可以启用的可选插件。
|
|
25
content/docs/theme.client.tsx
Normal file
25
content/docs/theme.client.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -73,7 +73,7 @@ Customise the max width of docs layout with CSS Variables.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
{/* <WidthTrigger /> */}
|
<WidthTrigger />
|
||||||
|
|
||||||
## Tailwind CSS Preset
|
## Tailwind CSS Preset
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ Fumadocs UI 有自己的颜色、动画和工具。
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
{/* <WidthTrigger /> */}
|
<WidthTrigger />
|
||||||
|
|
||||||
## Tailwind CSS 预设
|
## Tailwind CSS 预设
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
title: What is Fumadocs
|
title: What is Fumadocs
|
||||||
description: Introducing Fumadocs, a docs framework that you can break.
|
description: Introducing Fumadocs, a docs framework that you can break.
|
||||||
icon: CircleHelp
|
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**.
|
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.
|
**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.
|
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
|
## Why Fumadocs
|
||||||
|
|
||||||
Fumadocs is designed with flexibility in mind.
|
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.
|
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.
|
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!
|
You can also help Fumadocs to be more useful by contributing!
|
||||||
|
|
||||||
</PremiumContent>
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
title: 什么是 Fumadocs
|
title: 什么是 Fumadocs
|
||||||
description: 介绍 Fumadocs,一个可以打破常规的文档框架
|
description: 介绍 Fumadocs,一个可以打破常规的文档框架
|
||||||
icon: CircleHelp
|
icon: CircleHelp
|
||||||
premium: true
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Fumadocs 的创建是因为我想要一种更加可定制化的文档构建体验,一个不固执己见的文档框架,**一个你可以"打破"的"框架"**。
|
Fumadocs 的创建是因为我想要一种更加可定制化的文档构建体验,一个不固执己见的文档框架,**一个你可以"打破"的"框架"**。
|
||||||
@ -19,8 +18,6 @@ Fumadocs 的创建是因为我想要一种更加可定制化的文档构建体
|
|||||||
**对 UI 有自己的看法:** Fumadocs UI(默认主题)提供的唯一东西是**用户界面**。UI 的设计理念是提供更好的移动响应性和用户体验。
|
**对 UI 有自己的看法:** Fumadocs UI(默认主题)提供的唯一东西是**用户界面**。UI 的设计理念是提供更好的移动响应性和用户体验。
|
||||||
相反,我们使用受 Shadcn UI 启发的更灵活的方法 — [Fumadocs CLI](/docs/cli),这样我们可以快速迭代设计,并欢迎更多关于 UI 的反馈。
|
相反,我们使用受 Shadcn UI 启发的更灵活的方法 — [Fumadocs CLI](/docs/cli),这样我们可以快速迭代设计,并欢迎更多关于 UI 的反馈。
|
||||||
|
|
||||||
<PremiumContent>
|
|
||||||
|
|
||||||
## 为什么选择 Fumadocs
|
## 为什么选择 Fumadocs
|
||||||
|
|
||||||
Fumadocs 的设计考虑了灵活性。
|
Fumadocs 的设计考虑了灵活性。
|
||||||
@ -56,6 +53,4 @@ Fumadocs 为 Next.js 提供了额外的工具,包括语法高亮、文档搜
|
|||||||
|
|
||||||
Fumadocs 由 Fuma 和许多贡献者维护,关注代码库的可维护性。
|
Fumadocs 由 Fuma 和许多贡献者维护,关注代码库的可维护性。
|
||||||
虽然我们不打算提供人们想要的每一项功能,但我们更专注于使基本功能完美且维护良好。
|
虽然我们不打算提供人们想要的每一项功能,但我们更专注于使基本功能完美且维护良好。
|
||||||
您也可以通过贡献来帮助 Fumadocs 变得更加有用!
|
您也可以通过贡献来帮助 Fumadocs 变得更加有用!
|
||||||
|
|
||||||
</PremiumContent>
|
|
@ -1,8 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Cookie Policy
|
title: Cookie Policy
|
||||||
description: How we use cookies and similar technologies on our website
|
description: How we use cookies and similar technologies on our website
|
||||||
date: "2025-03-10"
|
date: 2025-03-10T00:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
|
locale: en
|
||||||
---
|
---
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
@ -56,4 +57,7 @@ We may update our Cookie Policy from time to time. We will notify you of any cha
|
|||||||
|
|
||||||
## Contact Us
|
## 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)
|
@ -1,8 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Cookie 政策
|
title: Cookie 政策
|
||||||
description: 我们如何在网站上使用 Cookie 和类似技术
|
description: 我们如何在网站上使用 Cookie 和类似技术
|
||||||
date: "2025-03-10"
|
date: 2025-03-10T00:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
|
locale: zh
|
||||||
---
|
---
|
||||||
|
|
||||||
## 引言
|
## 引言
|
||||||
@ -56,4 +57,7 @@ Cookie 是在您访问网站时存储在您设备上的小型文本文件。它
|
|||||||
|
|
||||||
## 联系我们
|
## 联系我们
|
||||||
|
|
||||||
如果您对本 Cookie 政策有任何疑问,请[联系我们](/contact)。
|
如果您对本 Cookie 政策有任何疑问,请联系我们:
|
||||||
|
|
||||||
|
- 通过电子邮件:support@example.com
|
||||||
|
- 通过访问我们的网站:[联系页面](https://example.com/contact)
|
@ -1,8 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Privacy Policy
|
title: Privacy Policy
|
||||||
description: Our commitment to protecting your privacy and personal data
|
description: Our commitment to protecting your privacy and personal data
|
||||||
date: "2025-03-10"
|
date: 2025-03-10T00:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
|
locale: en
|
||||||
---
|
---
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
@ -42,4 +43,7 @@ We may update our Privacy Policy from time to time. We will notify you of any ch
|
|||||||
|
|
||||||
## Contact Us
|
## 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)
|
@ -1,8 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: 隐私政策
|
title: 隐私政策
|
||||||
description: 我们致力于保护您的隐私和个人数据
|
description: 我们致力于保护您的隐私和个人数据
|
||||||
date: "2025-03-10"
|
date: 2025-03-10T00:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
|
locale: zh
|
||||||
---
|
---
|
||||||
|
|
||||||
## 引言
|
## 引言
|
||||||
@ -42,4 +43,7 @@ published: true
|
|||||||
|
|
||||||
## 联系我们
|
## 联系我们
|
||||||
|
|
||||||
如果您对本隐私政策有任何疑问,请[联系我们](/contact)。
|
如果您对本隐私政策有任何疑问,请联系我们:
|
||||||
|
|
||||||
|
- 通过电子邮件:support@example.com
|
||||||
|
- 通过访问我们的网站:[联系页面](https://example.com/contact)
|
@ -1,8 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: Terms of Service
|
title: Terms of Service
|
||||||
description: The terms and conditions governing the use of our services
|
description: The terms and conditions governing the use of our services
|
||||||
date: "2025-03-10"
|
date: 2025-03-10T00:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
|
locale: en
|
||||||
---
|
---
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
@ -48,4 +49,7 @@ We reserve the right to modify these Terms at any time. If we make changes, we w
|
|||||||
|
|
||||||
## Contact Us
|
## 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)
|
@ -1,8 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: 服务条款
|
title: 服务条款
|
||||||
description: 管理我们服务使用的条款和条件
|
description: 管理我们服务使用的条款和条件
|
||||||
date: "2025-03-10"
|
date: 2025-03-10T00:00:00.000Z
|
||||||
published: true
|
published: true
|
||||||
|
locale: zh
|
||||||
---
|
---
|
||||||
|
|
||||||
## 引言
|
## 引言
|
||||||
@ -48,4 +49,7 @@ published: true
|
|||||||
|
|
||||||
## 联系我们
|
## 联系我们
|
||||||
|
|
||||||
如果您对这些条款有任何疑问,请[联系我们](/contact)。
|
如果您对这些条款有任何疑问,请联系我们:
|
||||||
|
|
||||||
|
- 通过电子邮件:support@example.com
|
||||||
|
- 通过访问我们的网站:[联系页面](https://example.com/contact)
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: "Initial Release"
|
title: "Initial Release"
|
||||||
description: "Our first official release with core features and functionality"
|
description: "Our first official release with core features and functionality"
|
||||||
date: "2024-03-01"
|
date: "2024-03-01T00:00:00Z"
|
||||||
version: "v1.0.0"
|
version: "v1.0.0"
|
||||||
published: true
|
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
|
- Fixed issues with user registration flow
|
||||||
- Resolved authentication token expiration handling
|
- Resolved authentication token expiration handling
|
||||||
- Improved form validation and error messages
|
- Improved form validation and error messages
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: "初始版本"
|
title: "初始版本"
|
||||||
description: "我们的第一个正式版本,包含核心功能"
|
description: "我们的第一个正式版本,包含核心功能"
|
||||||
date: "2024-03-01"
|
date: "2024-03-01T00:00:00Z"
|
||||||
version: "v1.0.0"
|
version: "v1.0.0"
|
||||||
published: true
|
published: true
|
||||||
---
|
---
|
||||||
@ -27,4 +27,4 @@ published: true
|
|||||||
|
|
||||||
- 修复了用户注册流程中的问题
|
- 修复了用户注册流程中的问题
|
||||||
- 解决了身份验证令牌过期处理
|
- 解决了身份验证令牌过期处理
|
||||||
- 改进了表单验证和错误消息
|
- 改进了表单验证和错误消息
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: "Feature Update"
|
title: "Feature Update"
|
||||||
description: "New features and improvements to enhance your experience"
|
description: "New features and improvements to enhance your experience"
|
||||||
date: "2024-03-15"
|
date: "2024-03-15T00:00:00Z"
|
||||||
version: "v1.1.0"
|
version: "v1.1.0"
|
||||||
published: true
|
published: true
|
||||||
---
|
---
|
||||||
@ -27,4 +27,4 @@ We've added several new features to improve your experience:
|
|||||||
- Fixed issue with project duplication
|
- Fixed issue with project duplication
|
||||||
- Resolved calendar sync problems
|
- Resolved calendar sync problems
|
||||||
- Fixed data import validation errors
|
- Fixed data import validation errors
|
||||||
- Improved error handling for API requests
|
- Improved error handling for API requests
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: "功能更新"
|
title: "功能更新"
|
||||||
description: "新功能和改进,提升您的使用体验"
|
description: "新功能和改进,提升您的使用体验"
|
||||||
date: "2024-03-15"
|
date: "2024-03-15T00:00:00Z"
|
||||||
version: "v1.1.0"
|
version: "v1.1.0"
|
||||||
published: true
|
published: true
|
||||||
---
|
---
|
||||||
@ -27,4 +27,4 @@ published: true
|
|||||||
- 修复了项目复制问题
|
- 修复了项目复制问题
|
||||||
- 解决了日历同步问题
|
- 解决了日历同步问题
|
||||||
- 修复了数据导入验证错误
|
- 修复了数据导入验证错误
|
||||||
- 改进了API请求的错误处理
|
- 改进了API请求的错误处理
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: "AI Integration"
|
title: "AI Integration"
|
||||||
description: "Introducing AI-powered features to boost productivity"
|
description: "Introducing AI-powered features to boost productivity"
|
||||||
date: "2024-03-30"
|
date: "2024-03-30T00:00:00Z"
|
||||||
version: "v1.2.0"
|
version: "v1.2.0"
|
||||||
published: true
|
published: true
|
||||||
---
|
---
|
||||||
@ -34,4 +34,4 @@ We're thrilled to introduce our new AI capabilities:
|
|||||||
- Fixed issues with file uploads on certain browsers
|
- Fixed issues with file uploads on certain browsers
|
||||||
- Resolved synchronization issues between devices
|
- Resolved synchronization issues between devices
|
||||||
- Improved error handling for third-party integrations
|
- Improved error handling for third-party integrations
|
||||||
- Fixed accessibility issues in the dashboard
|
- Fixed accessibility issues in the dashboard
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: "AI集成"
|
title: "AI集成"
|
||||||
description: "引入AI驱动的功能,提高生产力"
|
description: "引入AI驱动的功能,提高生产力"
|
||||||
date: "2024-03-30"
|
date: "2024-03-30T00:00:00Z"
|
||||||
version: "v1.2.0"
|
version: "v1.2.0"
|
||||||
published: true
|
published: true
|
||||||
---
|
---
|
||||||
@ -34,4 +34,4 @@ published: true
|
|||||||
- 修复了某些浏览器上文件上传的问题
|
- 修复了某些浏览器上文件上传的问题
|
||||||
- 解决了设备之间的同步问题
|
- 解决了设备之间的同步问题
|
||||||
- 改进了第三方集成的错误处理
|
- 改进了第三方集成的错误处理
|
||||||
- 修复了仪表板中的可访问性问题
|
- 修复了仪表板中的可访问性问题
|
@ -1 +0,0 @@
|
|||||||
NEXTJS_ENV=development
|
|
@ -5,7 +5,7 @@ import { defineConfig } from 'drizzle-kit';
|
|||||||
* https://orm.drizzle.team/docs/get-started/neon-new#step-5---setup-drizzle-config-file
|
* https://orm.drizzle.team/docs/get-started/neon-new#step-5---setup-drizzle-config-file
|
||||||
*/
|
*/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
out: './src/db/migrations',
|
out: './drizzle',
|
||||||
schema: './src/db/schema.ts',
|
schema: './src/db/schema.ts',
|
||||||
dialect: 'postgresql',
|
dialect: 'postgresql',
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
|
@ -14,24 +14,6 @@ CREATE TABLE "account" (
|
|||||||
"updated_at" timestamp NOT NULL
|
"updated_at" timestamp NOT NULL
|
||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> 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" (
|
CREATE TABLE "session" (
|
||||||
"id" text PRIMARY KEY NOT NULL,
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
"expires_at" timestamp NOT NULL,
|
"expires_at" timestamp NOT NULL,
|
||||||
@ -41,7 +23,6 @@ CREATE TABLE "session" (
|
|||||||
"ip_address" text,
|
"ip_address" text,
|
||||||
"user_agent" text,
|
"user_agent" text,
|
||||||
"user_id" text NOT NULL,
|
"user_id" text NOT NULL,
|
||||||
"impersonated_by" text,
|
|
||||||
CONSTRAINT "session_token_unique" UNIQUE("token")
|
CONSTRAINT "session_token_unique" UNIQUE("token")
|
||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
@ -53,11 +34,6 @@ CREATE TABLE "user" (
|
|||||||
"image" text,
|
"image" text,
|
||||||
"created_at" timestamp NOT NULL,
|
"created_at" timestamp NOT NULL,
|
||||||
"updated_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")
|
CONSTRAINT "user_email_unique" UNIQUE("email")
|
||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> statement-breakpoint
|
||||||
@ -71,5 +47,4 @@ CREATE TABLE "verification" (
|
|||||||
);
|
);
|
||||||
--> statement-breakpoint
|
--> 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 "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;
|
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;
|
5
drizzle/0001_shocking_wild_pack.sql
Normal file
5
drizzle/0001_shocking_wild_pack.sql
Normal 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;
|
2
drizzle/0002_talented_greymalkin.sql
Normal file
2
drizzle/0002_talented_greymalkin.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE "user" ADD COLUMN "username" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" ADD CONSTRAINT "user_username_unique" UNIQUE("username");
|
2
drizzle/0003_lowly_dormammu.sql
Normal file
2
drizzle/0003_lowly_dormammu.sql
Normal 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");
|
2
drizzle/0004_known_khan.sql
Normal file
2
drizzle/0004_known_khan.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE "user" DROP CONSTRAINT "user_customer_id_unique";--> statement-breakpoint
|
||||||
|
ALTER TABLE "user" DROP COLUMN "customer_id";
|
1
drizzle/0005_modern_hex.sql
Normal file
1
drizzle/0005_modern_hex.sql
Normal 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
Loading…
Reference in New Issue
Block a user