目录

🤖 AI - 基于 Compoder 搭建公司的专属 AI Coding 平台

Compoder 是一个开源的 AI 驱动的组件代码生成引擎,集成了现代前端技术栈和多种 AI 模型的能力。

项目介绍

Compoder 是一个开源的 AI 驱动的组件代码生成引擎,集成了现代前端技术栈和多种 AI 模型的能力。如何基于 Compoder 定制特定技术栈(如:React)、特定组件库(如:Material UI、Ant Design、Shadcn UI、公司私有组件库…)以及特定场景(如:Landing Page)的 AI 驱动的组件代码生成器呢?

市场调研分析

传统路径下,由产品经理产出需求,交给 UI 设计师生成设计稿,前端工程师根据需求和设计稿开发业务组件,进行页面对接联调。

Compoder 助力前端工程师转型成为 AI 时代下的 Design Engineer,定制特定技术栈 & 特定组件库 & 特定场景下的前端组件代码生成器,通过 Prompt to code 高效生成对应的组件代码。

核心特性

支持定制多种技术栈、组件库、场景、代码规范、AI 模型等的组件代码生成器。

1、技术栈定制

定制基于特定技术栈(如:React)的 Codegen。

2、组件库定制

定制基于开源组件库 & 公司私有组件库(如:Material UI、Ant Design、Shadcn UI、公司私有组件库…)的 Codegen。

3、场景定制

定制基于特定场景(如:Landing Page)的 Codegen。

4、代码规范定制

定制基于特定代码规范(如:代码文件结构、设计风格…)的 Codegen。

5、AI 模型定制

定制基于多种 AI 模型(如:OpenAI、Claude…)的 Codegen。

基础功能

1、Prompt (文字、图片) To Code

输入文字或图片,即可生成组件代码。

2、代码版本迭代

支持代码版本迭代,可查看历史版本,并基于任意版本生成新的代码。

3、代码在线微调

支持代码在线微调,集成了代码编辑器,可以直观地对代码进行微调和保存。

4、代码实时预览

自建了一套代码实时预览沙箱环境,支持多种技术栈的秒级渲染。

技术栈

主要目录结构

[app/]                    // Next.js 应用主目录
├── main/                // 主界面相关页面
├── commons/            // 公共对接层组件
├── api/                // API 路由
├── services/           // 服务相关
└── layout.tsx          // 主布局组件

[components/]            // 组件目录
├── biz/                // 业务组件
├── ui/                 // UI 基础组件
└── provider/           // 上下文提供者组件

[lib/]                   // 核心库
├── auth/               // 认证相关
├── config/             // 配置相关
├── db/                 // 数据库相关
└── xml-message-parser/ // XML 解析工具

[artifacts/]             // 代码渲染沙箱环境
├── antd-renderer/      // Antd 渲染环境
├── shadcn-ui-renderer/ // Shadcn UI 渲染环境
└── material-ui-renderer/       // Material UI 渲染环境

技术架构设计

技术模块

数据库模块:

  • lib/db

前端模块:

  • components/ui
  • components/biz
  • app/**/*/page.tsx

后端模块:

  • app/api/codegen
  • app/api/componentCode

AI 模块:

  • app/api/ai-core

AI 友好的后端整洁分层

第一层:DB 层(lib/db/*)

  • types.ts - 数据表类型定义
  • schema.ts - schema 定义
  • selectors.ts - 数据查询
  • mutations.ts - 数据变更

第二层:API 层(app/api/*)

  • */types.d.ts - req、res 类型定义
  • */list/route.ts - 列表
  • */create/route.ts - 创建
  • */update/route.ts - 更新
  • */delete/route.ts - 删除

AI 友好的前端整洁分层

第一层:基础组件层(components/ui/*)

来源于 shadcn-ui 的组件。

  • Accordion
  • Button
  • Card

第二层:业务组件层(components/biz/*)

基于 shadcn-ui 基础组件 + 业务规则的二次封装。

  • index.ts - 导出组件
  • [ComponentName].tsx - 组件实现
  • [ComponentName]l.stories.tsx - 组件文档
  • interface.ts - 组件接口类型定义

第三层:API 请求服务层(app/services/*)

统一管理前端需要调用的 API 请求,包括请求参数类型、请求方法、请求路径、请求头、请求体、响应数据类型等。

第四层:server-store 层(app/**/*/server-store/*)

统一管理对应页面需要对接的 api 数据,包括数据变更、数据查询等。

第五层:页面层(app/**/*/page.tsx)

统一拼装对应页面需要对接的业务组件,给页面接入在 server-store 中管理的数据。

快速开始

环境准备

克隆仓库 & 安装依赖

# 克隆仓库
git clone git@github.com:OweQian/mini-compoder.git
cd mini-compoder

# 安装依赖
pnpm install

启动 Docker 容器

# docker 配置
cp docker-compose.template.yml docker-compose.yml

# 本地开发下,主要用来启动 MongoDB 数据库
docker compose up -d

# or
docker-compose up -d

环境变量 & 配置文件

# 填写对应的环境变量
cp .env.template .env

# Model provider 配置(需要更换其中的 BaseUrl、API Key)
cp data/config.template.json data/config.json

# Codegen 配置初始化
cp data/codegens.template.json data/codegens.json

pnpm migrate-codegen

启动 Storybook(可选)

pnpm storybook

启动 Compoder

pnpm dev

启动代码渲染沙箱(按需)

# 启动 Antd 渲染沙箱
cd artifacts/antd-renderer
pnpm dev

# 启动 Shadcn UI 渲染沙箱
cd artifacts/shadcn-ui-renderer
pnpm dev

# 启动 Material UI 渲染沙箱
cd artifacts/material-ui-renderer
pnpm dev

配置文件说明

Codegens Config

列出当前项目支持的前端组件代码生成方案及其规则,用于在应用中动态选择 Codegen、约束生成行为。

data/codegens.json

[
  {
    "title": "Shadcn/UI Codegen",
    "description": "Code generator based on Shadcn/UI",
    "fullStack": "React",
    "guides": [
      "Generate a login page",
      "Generate a Table component, include 3 columns: name, age, address"
    ],
    "model": "gpt-4o",
    "codeRendererUrl": "http://localhost:3001",
    "rules": [
      {
        "type": "public-components",
        "description": "Define which public components to use",
        "dataSet": ["shadcn/ui"]
      },
      {
        "type": "styles",
        "description": "Define the rules for generating styles",
        "prompt": "Styles must be written using tailwindcss with full dark/light mode compatibility. Use Tailwind's dark mode utilities (dark:class-name) for theme variants. Prefer using color-scheme-neutral classes or theme-specific variants (dark:bg-gray-800/light:bg-white) to ensure consistent appearance in both dark and light modes."
      },
      {
        "type": "attention-rules",
        "description": "Attention rules for the code generator",
        "prompt": "Only use the following npm packages in the generated code: react, react-dom, lucide-react, next/link, next/image, @/lib/utils, framer-motion, react-hook-form, recharts, zod, and components from @/components/ui/* (shadcn base components). Do not import or use any other packages. Specifically, DO NOT use @hookform/resolvers/zod package - instead, manually set up form validation with zod without using this resolver."
      }
    ]
  },
  {
    "title": "Ant Design Codegen",
    "description": "Code generator based on React and Ant Design",
    "fullStack": "React",
    "guides": [
      "Generate a login page",
      "Generate a Table component, include 3 columns: name, age, address"
    ],
    "model": "gpt-4o",
    "codeRendererUrl": "http://localhost:3002",
    "rules": [
      {
        "type": "public-components",
        "description": "Define which public components to use",
        "dataSet": ["antd", "@ant-design/icons"]
      },
      {
        "type": "styles",
        "description": "Define the rules for generating styles",
        "prompt": "Use styled-components for styles"
      },
      {
        "type": "file-structure",
        "description": "Define the file structure for the project",
        "prompt": "Output component code in XML format as follows:\n<ComponentArtifact name=\"ComponentName\">\n  <ComponentFile fileName=\"App.tsx\" isEntryFile=\"true\">\n    import { ComponentName } from './ComponentName';\n    \n    const mockProps = {\n      // Define mock data here\n    };\n    \n    export default function App() {\n      return <ComponentName {...mockProps} />;\n    }\n  </ComponentFile>\n  \n  <ComponentFile fileName=\"[ComponentName].tsx\">\n    // Main component implementation\n    // Split into multiple files if exceeds 500 lines\n    export const ComponentName = () => {\n      // Component implementation\n    }\n  </ComponentFile>\n\n  <ComponentFile fileName=\"helpers.ts\">\n    // Helper functions (optional)\n  </ComponentFile>\n\n  <ComponentFile fileName=\"interface.ts\">\n    // Type definitions for component props\n    // All API-interacting data must be defined as props:\n    // - initialData for component initialization\n    // - onChange, onSave, onDelete etc. for data modifications\n  </ComponentFile>\n  <ComponentFile fileName=\"styles.ts\">\n    // Styles for the component, must be written using styled-components only\n  </ComponentFile>\n</ComponentArtifact>"
      },
      {
        "type": "attention-rules",
        "description": "Attention rules for the code generator",
        "prompt": "Only use the following npm packages in the generated code: react, react-dom, antd, @ant-design/icons, styled-components. Do not import or use any other packages. When importing from 'antd' and '@ant-design/icons', ONLY use named imports like: import { Button, TextField } from 'antd'; import { Delete, Edit } from '@ant-design/icons'; - DO NOT use default imports, namespace imports, or side effect imports for these packages."
      }
    ]
  },
  {
    "title": "Mui Codegen",
    "description": "Code generator based on React and Mui",
    "fullStack": "React",
    "guides": [
      "Generate a login page",
      "Generate a Table component, include 3 columns: name, age, address"
    ],
    "model": "gpt-4o",
    "codeRendererUrl": "http://localhost:3003",
    "rules": [
      {
        "type": "public-components",
        "description": "Define which public components to use",
        "dataSet": ["@mui/material", "@mui/icons-material"]
      },
      {
        "type": "styles",
        "description": "Define the rules for generating styles",
        "prompt": "All styles must be written using sx prop only"
      },
      {
        "type": "attention-rules",
        "description": "Attention rules for the code generator",
        "prompt": "Only use the following npm packages in the generated code: react, react-dom, @mui/material, @mui/icons-material, @mui/x-date-pickers, @mui/x-tree-view. Do not import or use any other packages. When importing from '@mui/material' and '@mui/icons-material', ONLY use named imports like: import { Button, TextField } from '@mui/material'; import { Delete, Edit } from '@mui/icons-material'; - DO NOT use default imports, namespace imports, or side effect imports for these packages."
      }
    ]
  },
  {
    "title": "Private Component Codegen",
    "description": "Code generator based on private components",
    "fullStack": "React",
    "guides": [
      "Generate a login page",
      "Generate a Table component, include 3 columns: name, age, address"
    ],
    "model": "gpt-4o",
    "codeRendererUrl": "http://localhost:3005",
    "rules": [
      {
        "type": "styles",
        "description": "Define the rules for generating styles",
        "prompt": "Use styled-components for styles"
      },
      {
        "type": "file-structure",
        "description": "Define the file structure for the project",
        "prompt": "Output component code in XML format as follows:\n<ComponentArtifact name=\"ComponentName\">\n  <ComponentFile fileName=\"App.tsx\" isEntryFile=\"true\">\n    import { ComponentName } from './ComponentName';\n    \n    const mockProps = {\n      // Define mock data here\n    };\n    \n    export default function App() {\n      return <ComponentName {...mockProps} />;\n    }\n  </ComponentFile>\n  \n  <ComponentFile fileName=\"[ComponentName].tsx\">\n    // Main component implementation\n    // Split into multiple files if exceeds 500 lines\n    export const ComponentName = () => {\n      // Component implementation\n    }\n  </ComponentFile>\n\n  <ComponentFile fileName=\"helpers.ts\">\n    // Helper functions (optional)\n  </ComponentFile>\n\n  <ComponentFile fileName=\"interface.ts\">\n    // Type definitions for component props\n    // All API-interacting data must be defined as props:\n    // - initialData for component initialization\n    // - onChange, onSave, onDelete etc. for data modifications\n  </ComponentFile>\n  <ComponentFile fileName=\"styles.ts\">\n    // Styles for the component, must be written using styled-components only\n  </ComponentFile>\n</ComponentArtifact>"
      },
      {
        "type": "attention-rules",
        "description": "Attention rules for the code generator",
        "prompt": "Only use the following npm packages in the generated code: react, react-dom, @private-basic-components, styled-components. Do not import or use any other packages. When importing from '@private-basic-components', ONLY use named imports like: import { Button } from '@private-basic-components'; - DO NOT use default imports, namespace imports, or side effect imports for these packages."
      },
      {
        "type": "private-components",
        "description": "Define which private components to use",
        "docs": {
          "@private-basic-components": {
            "Affix": {
              "description": "On longer web pages, it's helpful to stick component into the viewport. This is common for menus and actions.\n\nPlease note that Affix should not cover other content on the page, especially when the size of the viewport is small.\n\n> Notes for developers\n>\n> After version `5.10.0`, we rewrite Affix use FC, Some methods of obtaining `ref` and calling internal instance methods will invalid.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| offsetBottom | Offset from the bottom of the viewport (in pixels) | number | - |\n| offsetTop | Offset from the top of the viewport (in pixels) | number | 0 |\n| target | Specifies the scrollable area DOM node | () => HTMLElement | () => window |\n| onChange | Callback for when Affix state is changed | (affixed?: boolean) => void | - |\n\n**Note:** Children of `Affix` must not have the property `position: absolute`, but you can set `position: absolute` on `Affix` itself:\n\n```jsx\n<Affix style={{ position: 'absolute', top: y, left: x }}>...</Affix>\n```"
            },
            "Alert": {
              "description": "- When you need to show alert messages to users.\n- When you need a persistent static container which is closable by user actions.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| action | The action of Alert | ReactNode | - | 4.9.0 |\n| afterClose | Called when close animation is finished | () => void | - |  |\n| banner | Whether to show as banner | boolean | false |  |\n| closeIcon | Custom close icon, >=5.7.0: close button will be hidden when setting to `null` or `false` | boolean \\| ReactNode | `<CloseOutlined />` |  |\n| description | Additional content of Alert | ReactNode | - |  |\n| icon | Custom icon, effective when `showIcon` is true | ReactNode | - |  |\n| message | Content of Alert | ReactNode | - |  |\n| showIcon | Whether to show icon | boolean | false, in `banner` mode default is true |  |\n| type | Type of Alert styles, options: `success`, `info`, `warning`, `error` | string | `info`, in `banner` mode default is `warning` |  |\n| onClose | Callback when Alert is closed | (e: MouseEvent) => void | - |  |\n\n### Alert.ErrorBoundary\n\n| Property    | Description                      | Type      | Default           | Version |\n| ----------- | -------------------------------- | --------- | ----------------- | ------- |\n| description | Custom error description to show | ReactNode | {{ error stack }} |         |\n| message     | Custom error message to show     | ReactNode | {{ error }}       |         |"
            },
            "Anchor": {
              "description": "For displaying anchor hyperlinks on page and jumping between them.\n\n> Notes for developers\n>\n> After version `4.24.0`, we rewrite Anchor use FC, Some methods of obtaining `ref` and calling internal instance methods will invalid.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Anchor Props\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| affix | Fixed mode of Anchor | boolean | true |  |\n| bounds | Bounding distance of anchor area | number | 5 |  |\n| getContainer | Scrolling container | () => HTMLElement | () => window |  |\n| getCurrentAnchor | Customize the anchor highlight | (activeLink: string) => string | - |  |\n| offsetTop | Pixels to offset from top when calculating position of scroll | number | 0 |  |\n| showInkInFixed | Whether show ink-square when `affix={false}` | boolean | false |  |\n| targetOffset | Anchor scroll offset, default as `offsetTop`, [example](#components-anchor-demo-targetoffset) | number | - |  |\n| onChange | Listening for anchor link change | (currentActiveLink: string) => void |  |  |\n| onClick | Set the handler to handle `click` event | (e: MouseEvent, link: object) => void | - |  |\n| items | Data configuration option content, support nesting through children | { key, href, title, target, children }\\[] [see](#anchoritem) | - | 5.1.0 |\n| direction | Set Anchor direction | `vertical` \\| `horizontal` | `vertical` | 5.2.0 |\n| replace | Replace items' href in browser history instead of pushing it | boolean | false | 5.7.0 |\n\n### AnchorItem\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| key | The unique identifier of the Anchor Link | string \\| number | - |  |\n| href | The target of hyperlink | string |  |  |\n| target | Specifies where to display the linked URL | string |  |  |\n| title | The content of hyperlink | ReactNode |  |  |\n| children | Nested Anchor Link, `Attention: This attribute does not support horizontal orientation` | [AnchorItem](#anchoritem)\\[] | - |  |\n| replace | Replace item href in browser history instead of pushing it | boolean | false | 5.7.0 |\n\n### Link Props\n\nWe recommend using the items form instead.\n\n| Property | Description                               | Type      | Default | Version |\n| -------- | ----------------------------------------- | --------- | ------- | ------- |\n| href     | The target of hyperlink                   | string    |         |         |\n| target   | Specifies where to display the linked URL | string    |         |         |\n| title    | The content of hyperlink                  | ReactNode |         |         |"
            },
            "App": {
              "description": "- Provide reset styles based on `.ant-app` element.\n- You could use static methods of `message/notification/Modal` form `useApp` without writing `contextHolder` manually.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### App\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| message | Global config for Message | [MessageConfig](/components/message/#messageconfig) | - | 5.3.0 |\n| notification | Global config for Notification | [NotificationConfig](/components/notification/#notificationconfig) | - | 5.3.0 |"
            },
            "AutoComplete": {
              "description": "- When you need an input box instead of a selector.\n- When you need input suggestions or helping text.\n\nThe differences with Select are:\n\n- AutoComplete is an input box with text hints, and users can type freely. The keyword is aiding **input**.\n- Select is selecting among given choices. The keyword is **select**.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| allowClear | Show clear button | boolean \\| { clearIcon?: ReactNode } | false | 5.8.0: Support Object type |\n| autoFocus | If get focus when component mounted | boolean | false |  |\n| backfill | If backfill selected item the input when using keyboard | boolean | false |  |\n| bordered | Whether has border style | boolean | true |  |\n| children (for customize input element) | Customize input element | HTMLInputElement \\| HTMLTextAreaElement \\| React.ReactElement&lt;InputProps> | &lt;Input /> |  |\n| children (for dataSource) | Data source to auto complete | React.ReactElement&lt;OptionProps> \\| Array&lt;React.ReactElement&lt;OptionProps>> | - |  |\n| defaultActiveFirstOption | Whether active first option by default | boolean | true |  |\n| defaultOpen | Initial open state of dropdown | boolean | - |  |\n| defaultValue | Initial selected option | string | - |  |\n| disabled | Whether disabled select | boolean | false |  |\n| popupClassName | The className of dropdown menu | string | - | 4.23.0 |\n| dropdownMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \\| number | true |  |\n| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns true, the option will be included in the filtered set; Otherwise, it will be excluded | boolean \\| function(inputValue, option) | true |  |\n| notFoundContent | Specify content to show when no result matches | string | `Not Found` |  |\n| open | Controlled open state of dropdown | boolean | - |  |\n| options | Select options. Will get better perf than jsx definition | { label, value }\\[] | - |  |\n| placeholder | The placeholder of input | string | - |  |\n| status | Set validation status | 'error' \\| 'warning' | - | 4.19.0 |\n| value | Selected option | string | - |  |\n| onBlur | Called when leaving the component | function() | - |  |\n| onChange | Called when selecting an option or changing an input value | function(value) | - |  |\n| onDropdownVisibleChange | Call when dropdown open | function(open) | - |  |\n| onFocus | Called when entering the component | function() | - |  |\n| onSearch | Called when searching items | function(value) | - |  |\n| onSelect | Called when a option is selected. param is option's value and option instance | function(value, option) | - |  |\n| onClear | Called when clear | function | - | 4.6.0 |"
            },
            "Badge": {
              "description": "Badge normally appears in proximity to notifications or user avatars with eye-catching appeal, typically displaying unread messages count.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Badge\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| color | Customize Badge dot color | string | - |  |\n| count | Number to show in badge | ReactNode | - |  |\n| classNames | Semantic DOM class | Record<SemanticDOM, string> | - | 5.7.0 |\n| dot | Whether to display a red dot instead of `count` | boolean | false |  |\n| offset | Set offset of the badge dot | \\[number, number] | - |  |\n| overflowCount | Max count to show | number | 99 |  |\n| showZero | Whether to show badge when `count` is zero | boolean | false |  |\n| size | If `count` is set, `size` sets the size of badge | `default` \\| `small` | - | - |\n| status | Set Badge as a status dot | `success` \\| `processing` \\| `default` \\| `error` \\| `warning` | - |  |\n| styles | Semantic DOM style | Record<SemanticDOM, CSSProperties> | - | 5.7.0 |\n| text | If `status` is set, `text` sets the display text of the status `dot` | ReactNode | - |  |\n| title | Text to show when hovering over the badge | string | - |  |\n\n### Badge.Ribbon\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| color | Customize Ribbon color | string | - |  |\n| placement | The placement of the Ribbon, `start` and `end` follow text direction (RTL or LTR) | `start` \\| `end` | `end` |  |\n| text | Content inside the Ribbon | ReactNode | - |  |\n\n### `styles` and `classNames` attribute\n\n| Property  | Description         | Version |\n| --------- | ------------------- | ------- |\n| root      | set `root` element  | 5.7.0   |\n| indicator | set `badge` element | 5.7.0   |"
            },
            "Breadcrumb": {
              "description": "- When the system has more than two layers in a hierarchy.\n- When you need to inform the user of where they are.\n- When the user may need to navigate back to a higher level.\n\n```jsx\n// works when >=5.3.0, recommended ✅\nreturn <Breadcrumb items={[{ title: 'sample' }]} />;\n\n// works when <5.3.0, deprecated when >=5.3.0 🙅🏻‍♀️\nreturn (\n  <Breadcrumb>\n    <Breadcrumb.Item>sample</Breadcrumb.Item>\n  </Breadcrumb>\n);\n\n// or\n\nreturn <Breadcrumb routes={[{ breadcrumbName: 'sample' }]} />;\n```",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Breadcrumb\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| itemRender | Custom item renderer | (route, params, routes, paths) => ReactNode | - |  |\n| params | Routing parameters | object | - |  |\n| items | The routing stack information of router | [items\\[\\]](#ItemType) | - | 5.3.0 |\n| separator | Custom separator | ReactNode | `/` |  |\n\n### ItemType\n\n> type ItemType = [RouteItemType](#RouteItemType) | [SeparatorType](#SeparatorType)\n\n### RouteItemType\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| className | The additional css class | string | - |  |\n| dropdownProps | The dropdown props | [Dropdown](/components/dropdown) | - |  |\n| href | Target of hyperlink. Can not work with `path` | string | - |  |\n| path | Connected path. Each path will connect with prev one. Can not work with `href` | string | - |  |\n| menu | The menu props | [MenuProps](/components/menu/#api) | - | 4.24.0 |\n| onClick | Set the handler to handle click event | (e:MouseEvent) => void | - |  |\n| title | item name | ReactNode | - |  |\n\n### SeparatorType\n\n```ts\nconst item = {\n  type: 'separator', // Must have\n  separator: '/',\n};\n```\n\n| Property  | Description       | Type        | Default | Version |\n| --------- | ----------------- | ----------- | ------- | ------- |\n| type      | Mark as separator | `separator` |         | 5.3.0   |\n| separator | Custom separator  | ReactNode   | `/`     | 5.3.0   |\n\n### Use with browserHistory\n\nThe link of Breadcrumb item targets `#` by default, you can use `itemRender` to make a `browserHistory` Link.\n\n```jsx\nimport { Link } from 'react-router';\n\nconst items = [\n  {\n    path: 'index',\n    title: 'home',\n  },\n  {\n    path: 'first',\n    title: 'first',\n    children: [\n      {\n        path: '/general',\n        title: 'General',\n      },\n      {\n        path: '/layout',\n        title: 'Layout',\n      },\n      {\n        path: '/navigation',\n        title: 'Navigation',\n      },\n    ],\n  },\n  {\n    path: 'second',\n    title: 'second',\n  },\n];\nfunction itemRender(route, params, items, paths) {\n  const last = items.indexOf(item) === items.length - 1;\n  return last ? <span>{item.title}</span> : <Link to={paths.join('/')}>{item.title}</Link>;\n}\n\nreturn <Breadcrumb itemRender={itemRender} items={items} />;\n```"
            },
            "Button": {
              "description": "A button means an operation (or a series of operations). Clicking a button will trigger corresponding business logic.\n\nIn @private-basic-components we provide 5 types of button.\n\n- Primary button: indicate the main action, one primary button at most in one section.\n- Default button: indicate a series of actions without priority.\n- Dashed button: commonly used for adding more actions.\n- Text button: used for the most secondary action.\n- Link button: used for external links.\n\nAnd 4 other properties additionally.\n\n- `danger`: used for actions of risk, like deletion or authorization.\n- `ghost`: used in situations with complex background, home pages usually.\n- `disabled`: when actions are not available.\n- `loading`: add loading spinner in button, avoiding multiple submits too.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\nDifferent button styles can be generated by setting Button properties. The recommended order is: `type` -> `shape` -> `size` -> `loading` -> `disabled`.\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| block | Option to fit button width to its parent width | boolean | false |  |\n| classNames | Semantic DOM class | Record<SemanticDOM, string> | - | 5.4.0 |\n| danger | Set the danger status of button | boolean | false |  |\n| disabled | Disabled state of button | boolean | false |  |\n| ghost | Make background transparent and invert text and border colors | boolean | false |  |\n| href | Redirect url of link button | string | - |  |\n| htmlType | Set the original html `type` of `button`, see: [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-type) | string | `button` |  |\n| icon | Set the icon component of button | ReactNode | - |  |\n| loading | Set the loading status of button | boolean \\| { delay: number } | false |  |\n| shape | Can be set button shape | `default` \\| `circle` \\| `round` | `default` |  |\n| size | Set the size of button | `large` \\| `middle` \\| `small` | `middle` |  |\n| styles | Semantic DOM style | Record<SemanticDOM, CSSProperties> | - | 5.4.0 |\n| target | Same as target attribute of a, works when href is specified | string | - |  |\n| type | Set button type | `primary` \\| `dashed` \\| `link` \\| `text` \\| `default` | `default` |  |\n| onClick | Set the handler to handle `click` event | (event: MouseEvent) => void | - |  |\n\nIt accepts all props which native buttons support.\n\n### `styles` and `classNames` attribute\n\n| Property | Description       | Version |\n| -------- | ----------------- | ------- |\n| icon     | set `icon`element | 5.5.0   |"
            },
            "Calendar": {
              "description": "When data is in the form of dates, such as schedules, timetables, prices calendar, lunar calendar. This component also supports Year/Month switch.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n**Note:** Part of the Calendar's locale is read from `value`. So, please set the locale of `dayjs` correctly.\n\n```jsx\n// The default locale is en-US, if you want to use other locale, just set locale in entry file globally.\n// import dayjs from 'dayjs';\n// import 'dayjs/locale/zh-cn';\n// dayjs.locale('zh-cn');\n\n<Calendar\n  dateCellRender={dateCellRender}\n  monthCellRender={monthCellRender}\n  onPanelChange={onPanelChange}\n  onSelect={onSelect}\n/>\n```\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| dateCellRender | Customize the display of the date cell, the returned content will be appended to the cell | function(date: Dayjs): ReactNode | - |  |\n| dateFullCellRender | Customize the display of the date cell, the returned content will override the cell | function(date: Dayjs): ReactNode | - |  |\n| defaultValue | The date selected by default | [dayjs](https://day.js.org/) | - |  |\n| disabledDate | Function that specifies the dates that cannot be selected, `currentDate` is same dayjs object as `value` prop which you shouldn't mutate it](https://github.com/@private-basic-components/@private-basic-components/issues/30987) | (currentDate: Dayjs) => boolean | - |  |\n| fullscreen | Whether to display in full-screen | boolean | true |  |\n| headerRender | Render custom header in panel | function(object:{value: Dayjs, type: string, onChange: f(), onTypeChange: f()}) | - |  |\n| locale | The calendar's locale | object | [(default)](https://github.com/@private-basic-components/@private-basic-components/blob/master/components/date-picker/locale/example.json) |  |\n| mode | The display mode of the calendar | `month` \\| `year` | `month` |  |\n| monthCellRender | Customize the display of the month cell, the returned content will be appended to the cell | function(date: Dayjs): ReactNode | - |  |\n| monthFullCellRender | Customize the display of the month cell, the returned content will override the cell | function(date: Dayjs): ReactNode | - |  |\n| validRange | To set valid range | \\[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - |  |\n| value | The current selected date | [dayjs](https://day.js.org/) | - |  |\n| onChange | Callback for when date changes | function(date: Dayjs) | - |  |\n| onPanelChange | Callback for when panel changes | function(date: Dayjs, mode: string) | - |  |\n| onSelect | Callback for when a date is selected, include source info | function(date: Dayjs, info: { source: 'year' \\| 'month' \\| 'date' \\| 'customize' }) | - | `info`: 5.6.0 |"
            },
            "Card": {
              "description": "A card can be used to display content related to a single subject. The content can consist of multiple elements of varying types and sizes.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n```jsx\n<Card title=\"Card title\">Card content</Card>\n```\n\n### Card\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| actions | The action list, shows at the bottom of the Card | Array&lt;ReactNode> | - |  |\n| activeTabKey | Current TabPane's key | string | - |  |\n| bodyStyle | Inline style to apply to the card content | CSSProperties | - |  |\n| bordered | Toggles rendering of the border around the card | boolean | true |  |\n| cover | Card cover | ReactNode | - |  |\n| defaultActiveTabKey | Initial active TabPane's key, if `activeTabKey` is not set | string | - |  |\n| extra | Content to render in the top-right corner of the card | ReactNode | - |  |\n| headStyle | Inline style to apply to the card head | CSSProperties | - |  |\n| hoverable | Lift up when hovering card | boolean | false |  |\n| loading | Shows a loading indicator while the contents of the card are being fetched | boolean | false |  |\n| size | Size of card | `default` \\| `small` | `default` |  |\n| tabBarExtraContent | Extra content in tab bar | ReactNode | - |  |\n| tabList | List of TabPane's head | [TabItemType](/components/tabs#tabitemtype)[] | - |  |\n| tabProps | [Tabs](/components/tabs/#tabs) | - | - |  |\n| title | Card title | ReactNode | - |  |\n| type | Card style type, can be set to `inner` or not set | string | - |  |\n| onTabChange | Callback when tab is switched | (key) => void | - |  |\n\n### Card.Grid\n\n| Property  | Description                     | Type          | Default | Version |\n| --------- | ------------------------------- | ------------- | ------- | ------- |\n| className | The className of container      | string        | -       |         |\n| hoverable | Lift up when hovering card grid | boolean       | true    |         |\n| style     | The style object of container   | CSSProperties | -       |         |\n\n### Card.Meta\n\n| Property    | Description                   | Type          | Default | Version |\n| ----------- | ----------------------------- | ------------- | ------- | ------- |\n| avatar      | Avatar or icon                | ReactNode     | -       |         |\n| className   | The className of container    | string        | -       |         |\n| description | Description content           | ReactNode     | -       |         |\n| style       | The style object of container | CSSProperties | -       |         |\n| title       | Title content                 | ReactNode     | -       |         |"
            },
            "Carousel": {
              "description": "- When there is a group of content on the same level.\n- When there is insufficient content space, it can be used to save space in the form of a revolving door.\n- Commonly used for a group of pictures/cards.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| autoplay | Whether to scroll automatically | boolean | false |  |\n| dotPosition | The position of the dots, which can be one of `top` `bottom` `left` `right` | string | `bottom` |  |\n| dots | Whether to show the dots at the bottom of the gallery, `object` for `dotsClass` and any others | boolean \\| { className?: string } | true |  |\n| waitForAnimate | Whether to wait for the animation when switching | boolean | false |  |\n| easing | Transition interpolation function name | string | `linear` |  |\n| effect | Transition effect | `scrollx` \\| `fade` | `scrollx` |  |\n| afterChange | Callback function called after the current index changes | (current: number) => void | - |  |\n| beforeChange | Callback function called before the current index changes | (current: number, next: number) => void | - |  |"
            },
            "Cascader": {
              "description": "- When you need to select from a set of associated data set. Such as province/city/district, company level, things classification.\n- When selecting from a large data set, with multi-stage classification separated for easy selection.\n- Chooses cascade items in one float layer for better user experience.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n```jsx\n<Cascader options={options} onChange={onChange} />\n```\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| allowClear | Show clear button | boolean \\| { clearIcon?: ReactNode } | true | 5.8.0: Support object type |\n| autoClearSearchValue | Whether the current search will be cleared on selecting an item. Only applies when `multiple` is `true` | boolean | true | 5.9.0 |\n| autoFocus | If get focus when component mounted | boolean | false |  |\n| bordered | Whether has border style | boolean | true |  |\n| changeOnSelect | (Work on single select) Change value on each selection if set to true, see above demo for details | boolean | false |  |\n| className | The additional css class | string | - |  |\n| defaultValue | Initial selected value | string\\[] \\| number\\[] | \\[] |  |\n| disabled | Whether disabled select | boolean | false |  |\n| displayRender | The render function of displaying selected options | (label, selectedOptions) => ReactNode | label => label.join(`/`) | `multiple`: 4.18.0 |\n| tagRender | Custom render function for tags in `multiple` mode | (label: string, onClose: function, value: string) => ReactNode | - |  |\n| popupClassName | The additional className of popup overlay | string | - | 4.23.0 |\n| dropdownRender | Customize dropdown content | (menus: ReactNode) => ReactNode | - | 4.4.0 |\n| expandIcon | Customize the current item expand icon | ReactNode | - | 4.4.0 |\n| expandTrigger | expand current item when click or hover, one of `click` `hover` | string | `click` |  |\n| fieldNames | Custom field name for label and value and children | object | { label: `label`, value: `value`, children: `children` } |  |\n| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative. [example](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | function(triggerNode) | () => document.body |  |\n| loadData | To load option lazily, and it cannot work with `showSearch` | (selectedOptions) => void | - |  |\n| maxTagCount | Max tag count to show. `responsive` will cost render performance | number \\| `responsive` | - | 4.17.0 |\n| maxTagPlaceholder | Placeholder for not showing tags | ReactNode \\| function(omittedValues) | - | 4.17.0 |\n| maxTagTextLength | Max tag text length to show | number | - | 4.17.0 |\n| notFoundContent | Specify content to show when no result matches | string | `Not Found` |  |\n| open | Set visible of cascader popup | boolean | - | 4.17.0 |\n| options | The data options of cascade | [Option](#option)\\[] | - |  |\n| placeholder | The input placeholder | string | `Please select` |  |\n| placement | Use preset popup align config from builtinPlacements | `bottomLeft` `bottomRight` `topLeft` `topRight` | `bottomLeft` | 4.17.0 |\n| showSearch | Whether show search input in single mode | boolean \\| [Object](#showsearch) | false |  |\n| size | The input size | `large` \\| `middle` \\| `small` | - |  |\n| status | Set validation status | 'error' \\| 'warning' | - | 4.19.0 |\n| style | The additional style | CSSProperties | - |  |\n| suffixIcon | The custom suffix icon | ReactNode | - |  |\n| value | The selected value | string\\[] \\| number\\[] | - |  |\n| onChange | Callback when finishing cascader select | (value, selectedOptions) => void | - |  |\n| onDropdownVisibleChange | Callback when popup shown or hidden | (value) => void | - | 4.17.0 |\n| multiple | Support multiple or not | boolean | - | 4.17.0 |\n| removeIcon | The custom remove icon | ReactNode | - |  |\n| showCheckedStrategy | The way show selected item in box. ** `SHOW_CHILD`: ** just show child treeNode. **`Cascader.SHOW_PARENT`:** just show parent treeNode (when all child treeNode under the parent treeNode are checked) | `Cascader.SHOW_PARENT` \\| `Cascader.SHOW_CHILD` | `Cascader.SHOW_PARENT` | 4.20.0 |\n| searchValue | Set search value, Need work with `showSearch` | string | - | 4.17.0 |\n| onSearch | The callback function triggered when input changed | (search: string) => void | - | 4.17.0 |\n| dropdownMenuColumnStyle | The style of the drop-down menu column | CSSProperties | - |  |\n| loadingIcon | The appearance of lazy loading (now is useless) | ReactNode | - |  |\n\n### showSearch\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| filter | The function will receive two arguments, inputValue and option, if the function returns true, the option will be included in the filtered set; Otherwise, it will be excluded | function(inputValue, path): boolean | - |  |\n| limit | Set the count of filtered items | number \\| false | 50 |  |\n| matchInputWidth | Whether the width of list matches input, ([how it looks](https://github.com/@private-basic-components/@private-basic-components/issues/25779)) | boolean | true |  |\n| render | Used to render filtered options | function(inputValue, path): ReactNode | - |  |\n| sort | Used to sort filtered options | function(a, b, inputValue) | - |  |\n\n### Option\n\n```typescript\ninterface Option {\n  value: string | number;\n  label?: React.ReactNode;\n  disabled?: boolean;\n  children?: Option[];\n  // Determines if this is a leaf node(effective when `loadData` is specified).\n  // `false` will force trade TreeNode as a parent node.\n  // Show expand icon even if the current node has no children.\n  isLeaf?: boolean;\n}\n```"
            },
            "Checkbox": {
              "description": "- Used for selecting multiple values from several options.\n- If you use only one checkbox, it is the same as using Switch to toggle between two states. The difference is that Switch will trigger the state change directly, but Checkbox just marks the state as changed and this needs to be submitted.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n#### Checkbox\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| autoFocus | If get focus when component mounted | boolean | false |  |\n| checked | Specifies whether the checkbox is selected | boolean | false |  |\n| defaultChecked | Specifies the initial state: whether or not the checkbox is selected | boolean | false |  |\n| disabled | If disable checkbox | boolean | false |  |\n| indeterminate | The indeterminate checked state of checkbox | boolean | false |  |\n| onChange | The callback function that is triggered when the state changes | (e: CheckboxChangeEvent) => void | - |  |\n\n#### Checkbox Group\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| defaultValue | Default selected value | (string \\| number)\\[] | \\[] |  |\n| disabled | If disable all checkboxes | boolean | false |  |\n| name | The `name` property of all `input[type=\"checkbox\"]` children | string | - |  |\n| options | Specifies options | string\\[] \\| number\\[] \\| Option\\[] | \\[] |  |\n| value | Used for setting the currently selected value | (string \\| number \\| boolean)\\[] | \\[] |  |\n| onChange | The callback function that is triggered when the state changes | (checkedValue: CheckboxValueType[]) => void | - |  |\n\n##### Option\n\n```typescript\ninterface Option {\n  label: string;\n  value: string;\n  disabled?: boolean;\n}\n```\n\n### Methods\n\n#### Checkbox\n\n| Name    | Description  | Version |\n| ------- | ------------ | ------- |\n| blur()  | Remove focus |         |\n| focus() | Get focus    |         |"
            },
            "Collapse": {
              "description": "- Can be used to group or hide complex regions to keep the page clean.\n- `Accordion` is a special kind of `Collapse`, which allows only one panel to be expanded at a time.\n\n```tsx | pure\n// works when >= 5.6.0, recommended ✅\nconst text = `\n  A dog is a type of domesticated animal.\n  Known for its loyalty and faithfulness,\n  it can be found as a welcome guest in many households across the world.\n`;\n\nconst items: CollapseProps['items'] = [\n  {\n    key: '1',\n    label: 'This is panel header 1',\n    children: <p>{text}</p>,\n  },\n  {\n    key: '2',\n    label: 'This is panel header 2',\n    children: <p>{text}</p>,\n  },\n  {\n    key: '3',\n    label: 'This is panel header 3',\n    children: <p>{text}</p>,\n  },\n];\n\n<Collapse items={items} defaultActiveKey={['1']} />;\n\n// works when <5.6.0 , deprecated when >=5.6.0  🙅🏻‍♀️\n\n<Collapse defaultActiveKey={['1']} onChange={onChange}>\n  <Panel header=\"This is panel header 1\" key=\"1\">\n    <p>{text}</p>\n  </Panel>\n  <Panel header=\"This is panel header 2\" key=\"2\">\n    <p>{text}</p>\n  </Panel>\n  <Panel header=\"This is panel header 3\" key=\"3\">\n    <p>{text}</p>\n  </Panel>\n</Collapse>;\n```",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Collapse\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| accordion | If true, Collapse renders as Accordion | boolean | false |  |\n| activeKey | Key of the active panel | string\\[] \\| string <br/> number\\[] \\| number | No default value. In `accordion` mode, it's the key of the first panel |  |\n| bordered | Toggles rendering of the border around the collapse block | boolean | true |  |\n| collapsible | Specify whether the panels of children be collapsible or the trigger area of collapsible | `header` \\| `icon` \\| `disabled` | - | 4.9.0 |\n| defaultActiveKey | Key of the initial active panel | string\\[] \\| string <br/> number\\[] \\| number | - |  |\n| destroyInactivePanel | Destroy Inactive Panel | boolean | false |  |\n| expandIcon | Allow to customize collapse icon | (panelProps) => ReactNode | - |  |\n| expandIconPosition | Set expand icon position | `start` \\| `end` | - | 4.21.0 |\n| ghost | Make the collapse borderless and its background transparent | boolean | false | 4.4.0 |\n| size | Set the size of collapse | `large` \\| `middle` \\| `small` | `middle` | 5.2.0 |\n| onChange | Callback function executed when active panel is changed | function | - |  |\n| items | collapse items content | [ItemType](https://github.com/react-component/collapse/blob/27250ca5415faab16db412b9bff2c131bb4f32fc/src/interface.ts#L6) | - | 5.6.0 |\n\n### Collapse.Panel\n\n<Alert message=\"&gt;= 5.6.0 configure the panel by `items`.\"></Alert>\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| collapsible | Specify whether the panel be collapsible or the trigger area of collapsible | `header` \\| `icon` \\| `disabled` | - | 4.9.0 (icon: 4.24.0) |\n| extra | The extra element in the corner | ReactNode | - |  |\n| forceRender | Forced render of content on panel, instead of lazy rendering after clicking on header | boolean | false |  |\n| header | Title of the panel | ReactNode | - |  |\n| key | Unique key identifying the panel from among its siblings | string \\| number | - |  |\n| showArrow | If false, panel will not show arrow icon. If false, collapsible can't be set as icon | boolean | true |  |"
            },
            "ColorPicker": {
              "description": "Used when the user needs to customize the color selection.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n> This component is available since `@private-basic-components@5.5.0`.\n\n<!-- prettier-ignore -->\n| Property | Description | Type | Default | Version |\n| :-- | :-- | :-- | :-- | :-- |\n| allowClear | \tAllow clearing color selected | boolean | false | |\n| arrow | Configuration for popup arrow | `boolean \\| { pointAtCenter: boolean }` | true | |\n| children | Trigger of ColorPicker | React.ReactNode | - | |\n| defaultValue | Default value of color | string \\| `Color` | - | |\n| defaultFormat | Default format of color | `rgb` \\| `hex` \\| `hsb` | - | 5.9.0 |\n| disabled | Disable ColorPicker | boolean | - | |\n| disabledAlpha | Disable Alpha | boolean | - | 5.8.0 |\n| destroyTooltipOnHide | Whether destroy popover when hidden | `boolean` | false | 5.7.0 |\n| format | Format of color | `rgb` \\| `hex` \\| `hsb` | `hex` | |\n| open | Whether to show popup | boolean | - | |\n| presets | \tPreset colors | `{ label: ReactNode, colors: Array<string \\| Color> }[]` | - | |\n| placement | Placement of popup | `top` \\| `topLeft` \\| `topRight` \\| `bottom` \\| `bottomLeft` \\| `bottomRight` | `bottomLeft` | |\n| panelRender | Custom Render Panel | `(panel: React.ReactNode, extra: { components: { Picker: FC; Presets: FC } }) => React.ReactNode` | - | 5.7.0 |\n| showText | Show color text | boolean \\| `(color: Color) => React.ReactNode` | - | 5.7.0 |\n| size | Setting the trigger size | `large` \\| `middle` \\| `small` | `middle` | 5.7.0 |\n| trigger | ColorPicker trigger mode | `hover` \\| `click` | `click` | |\n| value | Value of color | string \\| `Color` | - | |\n| onChange | Callback when `value` is changed | `(value: Color, hex: string) => void` | - | |\n| onChangeComplete | Called when color pick ends   | `(value: Color) => void` | - | 5.7.0 |\n| onFormatChange | Callback when `format` is changed | `(format: 'hex' \\| 'rgb' \\| 'hsb') => void` | - | |\n| onOpenChange | Callback when `open` is changed | `(open: boolean) => void` | - | |\n| onClear | Called when clear | `() => void` | - | 5.6.0 |\n\n### Color\n\n<!-- prettier-ignore -->\n| Property | Description | Type | Default |\n| :-- | :-- | :-- | :-- |\n| toHex | Convert to `hex` format characters, the return type like: `1677ff` | `() => string` | - |\n| toHexString | Convert to `hex` format color string, the return type like: `#1677ff` | `() => string` | - |\n| toHsb | Convert to `hsb` object  | `() => ({ h: number, s: number, b: number, a number })` | - |\n| toHsbString | Convert to `hsb` format color string, the return type like: `hsb(215, 91%, 100%)` | `() => string` | - |\n| toRgb | Convert to `rgb` object  | `() => ({ r: number, g: number, b: number, a number })` | - |\n| toRgbString | Convert to `rgb` format color string, the return type like: `rgb(22, 119, 255)` | `() => string` | - |"
            },
            "DatePicker": {
              "description": "By clicking the input box, you can select a date from a popup calendar.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\nThere are five kinds of picker:\n\n- DatePicker\n- DatePicker\\[picker=\"month\"]\n- DatePicker\\[picker=\"week\"]\n- DatePicker\\[picker=\"year\"]\n- DatePicker\\[picker=\"quarter\"] (Added in 4.1.0)\n- RangePicker\n\n### Localization\n\nThe default locale is en-US, if you need to use other languages, recommend to use internationalized components provided by us at the entrance. Look at: [ConfigProvider](https://@private-basic-components/components/config-provider/).\n\nIf there are special needs (only modifying single component language), Please use the property: local. Example: [default](https://github.com/@private-basic-components/@private-basic-components/blob/master/components/date-picker/locale/example.json).\n\n```jsx\nimport locale from '@private-basic-components/es/date-picker/locale/zh_CN';\n\nimport 'dayjs/locale/zh-cn';\n\n<DatePicker locale={locale} />;\n```\n\n```jsx\n// The default locale is en-US, if you want to use other locale, just set locale in entry file globally.\nimport locale from '@private-basic-components/locale/zh_CN';\nimport dayjs from 'dayjs';\n\nimport 'dayjs/locale/zh-cn';\n\n<ConfigProvider locale={locale}>\n  <DatePicker defaultValue={dayjs('2015-01-01', 'YYYY-MM-DD')} />\n</ConfigProvider>;\n```\n\n### Common API\n\nThe following APIs are shared by DatePicker, RangePicker.\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| allowClear | Customize clear button | boolean \\| { clearIcon?: ReactNode } | true | 5.8.0: Support object type |\n| autoFocus | If get focus when component mounted | boolean | false |  |\n| bordered | Whether has border style | boolean | true |  |\n| className | The picker className | string | - |  |\n| dateRender | Custom rendering function for date cells, >= 5.4.0 use `cellRender` instead. | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.0 |\n| changeOnBlur | Trigger `change` when blur. e.g. datetime picker no need click confirm button | boolean | false | 5.5.0 |\n| cellRender | Custom rendering function for picker cells | (current: dayjs, info: { originNode: React.ReactElement,today: DateType, range?: 'start' \\| 'end', type: PanelMode, locale?: Locale, subType?: 'hour' \\| 'minute' \\| 'second' \\| 'meridiem' }) => React.ReactNode | - | 5.4.0 |\n| disabled | Determine whether the DatePicker is disabled | boolean | false |  |\n| disabledDate | Specify the date that cannot be selected | (currentDate: dayjs) => boolean | - |  |\n| format | To set the date format, support multi-format matching when it is an array, display the first one shall prevail. refer to [dayjs#format](https://day.js.org/docs/en/display/format). for example: [Custom Format](#components-date-picker-demo-format) | [formatType](#formattype) | [rc-picker](https://github.com/react-component/picker/blob/f512f18ed59d6791280d1c3d7d37abbb9867eb0b/src/utils/uiUtil.ts#L155-L177) |  |\n| popupClassName | To customize the className of the popup calendar | string | - | 4.23.0 |\n| getPopupContainer | To set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - |  |\n| inputReadOnly | Set the `readonly` attribute of the input tag (avoids virtual keyboard on touch devices) | boolean | false |  |\n| locale | Localization configuration | object | [default](https://github.com/@private-basic-components/@private-basic-components/blob/master/components/date-picker/locale/example.json) |  |\n| mode | The picker panel mode( [Cannot select year or month anymore?](/docs/react/faq#when-set-mode-to-datepickerrangepicker-cannot-select-year-or-month-anymore) ) | `time` \\| `date` \\| `month` \\| `year` \\| `decade` | - |  |\n| nextIcon | The custom next icon | ReactNode | - | 4.17.0 |\n| open | The open state of picker | boolean | - |  |\n| panelRender | Customize panel render | (panelNode) => ReactNode | - | 4.5.0 |\n| picker | Set picker type | `date` \\| `week` \\| `month` \\| `quarter` \\| `year` | `date` | `quarter`: 4.1.0 |\n| placeholder | The placeholder of date input | string \\| \\[string,string] | - |  |\n| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft |  |\n| popupStyle | To customize the style of the popup calendar | CSSProperties | {} |  |\n| presets | The preset ranges for quick selection, Since `5.8.0`, preset value supports callback function. | { label: React.ReactNode, value: Dayjs \\| (() => Dayjs) }\\[] | - |  |\n| prevIcon | The custom prev icon | ReactNode | - | 4.17.0 |\n| size | To determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | `large` \\| `middle` \\| `small` | - |  |\n| status | Set validation status | 'error' \\| 'warning' | - | 4.19.0 |\n| style | To customize the style of the input box | CSSProperties | {} |  |\n| suffixIcon | The custom suffix icon | ReactNode | - |  |\n| superNextIcon | The custom super next icon | ReactNode | - | 4.17.0 |\n| superPrevIcon | The custom super prev icon | ReactNode | - | 4.17.0 |\n| onOpenChange | Callback function, can be executed whether the popup calendar is popped up or closed | function(open) | - |  |\n| onPanelChange | Callback when picker panel mode is changed | function(value, mode) | - |  |\n\n### Common Methods\n\n| Name    | Description  | Version |\n| ------- | ------------ | ------- |\n| blur()  | Remove focus |         |\n| focus() | Get focus    |         |\n\n### DatePicker\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| defaultValue | To set default date, if start time or end time is null or undefined, the date range will be an open interval | [dayjs](https://day.js.org/) | - |  |\n| disabledTime | To specify the time that cannot be selected | function(date) | - |  |\n| format | To set the date format. refer to [dayjs#format](https://day.js.org/docs/en/display/format) | [formatType](#formattype) | `YYYY-MM-DD` |  |\n| renderExtraFooter | Render extra footer in panel | (mode) => React.ReactNode | - |  |\n| showNow | Whether to show 'Now' button on panel when `showTime` is set | boolean | - | 4.4.0 |\n| showTime | To provide an additional time selection | object \\| boolean | [TimePicker Options](/components/time-picker/#api) |  |\n| showTime.defaultValue | To set default time of selected date, [demo](#components-date-picker-demo-disabled-date) | [dayjs](https://day.js.org/) | dayjs() |  |\n| showToday | Whether to show `Today` button | boolean | true |  |\n| value | To set date | [dayjs](https://day.js.org/) | - |  |\n| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - |  |\n| onOk | Callback when click ok button | function() | - |  |\n| onPanelChange | Callback function for panel changing | function(value, mode) | - |  |\n\n### DatePicker\\[picker=year]\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| defaultValue | To set default date | [dayjs](https://day.js.org/) | - |  |\n| format | To set the date format. refer to [dayjs#format](https://day.js.org/docs/en/display/format) | [formatType](#formattype) | `YYYY` |  |\n| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - |  |\n| value | To set date | [dayjs](https://day.js.org/) | - |  |\n| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - |  |\n\n### DatePicker\\[picker=quarter]\n\nAdded in `4.1.0`.\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| defaultValue | To set default date | [dayjs](https://day.js.org/) | - |  |\n| format | To set the date format. refer to [dayjs#format](https://day.js.org/docs/en/display/format) | [formatType](#formattype) | `YYYY-\\QQ` |  |\n| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - |  |\n| value | To set date | [dayjs](https://day.js.org/) | - |  |\n| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - |  |\n\n### DatePicker\\[picker=month]\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| defaultValue | To set default date | [dayjs](https://day.js.org/) | - |  |\n| format | To set the date format. refer to [dayjs#format](https://day.js.org/docs/en/display/format) | [formatType](#formattype) | `YYYY-MM` |  |\n| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - |  |\n| value | To set date | [dayjs](https://day.js.org/) | - |  |\n| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - |  |\n\n### DatePicker\\[picker=week]\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| defaultValue | To set default date | [dayjs](https://day.js.org/) | - |  |\n| format | To set the date format. refer to [dayjs#format](https://day.js.org/docs/en/display/format) | [formatType](#formattype) | `YYYY-wo` |  |\n| renderExtraFooter | Render extra footer in panel | (mode) => React.ReactNode | - |  |\n| value | To set date | [dayjs](https://day.js.org/) | - |  |\n| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - |  |\n\n### RangePicker\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| allowEmpty | Allow start or end input leave empty | \\[boolean, boolean] | \\[false, false] |  |\n| dateRender | Custom rendering function for date cells, >= 5.4.0 use `cellRender` instead. | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | < 5.4.0 |\n| cellRender | Custom rendering function for picker cells | (current: dayjs, info: { originNode: React.ReactElement,today: DateType, range?: 'start' \\| 'end', type: PanelMode, locale?: Locale, subType?: 'hour' \\| 'minute' \\| 'second' \\| 'meridiem' }) => React.ReactNode | - | 5.4.0 |\n| defaultValue | To set default date | \\[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - |  |\n| disabled | If disable start or end | \\[boolean, boolean] | - |  |\n| disabledTime | To specify the time that cannot be selected | function(date: dayjs, partial: `start` \\| `end`) | - |  |\n| format | To set the date format. refer to [dayjs#format](https://day.js.org/docs/en/display/format) | [formatType](#formattype) | `YYYY-MM-DD HH:mm:ss` |  |\n| presets | The preset ranges for quick selection, Since `5.8.0`, preset value supports callback function. | { label: React.ReactNode, value: (Dayjs \\| (() => Dayjs))\\[] }\\[] | - |  |\n| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - |  |\n| separator | Set separator between inputs | React.ReactNode | `<SwapRightOutlined />` |  |\n| showTime | To provide an additional time selection | object \\| boolean | [TimePicker Options](/components/time-picker/#api) |  |\n| showTime.defaultValue | To set default time of selected date, [demo](#components-date-picker-demo-disabled-date) | [dayjs](https://day.js.org/)\\[] | \\[dayjs(), dayjs()] |  |\n| value | To set date | \\[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - |  |\n| onCalendarChange | Callback function, can be executed when the start time or the end time of the range is changing. `info` argument is added in 4.4.0 | function(dates: \\[dayjs, dayjs], dateStrings: \\[string, string], info: { range:`start`\\|`end` }) | - |  |\n| onChange | Callback function, can be executed when the selected time is changing | function(dates: \\[dayjs, dayjs], dateStrings: \\[string, string]) | - |  |\n\n#### formatType\n\n```typescript\nimport type { Dayjs } from 'dayjs';\n\ntype Generic = string;\ntype GenericFn = (value: Dayjs) => string;\n\nexport type FormatType = Generic | GenericFn | Array<Generic | GenericFn>;\n```"
            },
            "Descriptions": {
              "description": "Commonly displayed on the details page.\n\n```tsx | pure\n// works when >= 5.8.0, recommended ✅\n\nconst items: DescriptionsProps['items'] = [\n  {\n    key: '1',\n    label: 'UserName',\n    children: <p>Zhou Maomao</p>,\n  },\n  {\n    key: '2',\n    label: 'Telephone',\n    children: <p>1810000000</p>,\n  },\n  {\n    key: '3',\n    label: 'Live',\n    children: <p>Hangzhou, Zhejiang</p>,\n  },\n  {\n    key: '4',\n    label: 'Remark',\n    children: <p>empty</p>,\n  },\n  {\n    key: '5',\n    label: 'Address',\n    children: <p>No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China</p>,\n  },\n];\n\n<Descriptions title=\"User Info\" items={items} />;\n\n// works when <5.8.0 , deprecated when >=5.8.0 🙅🏻‍♀️\n\n<Descriptions title=\"User Info\">\n  <Descriptions.Item label=\"UserName\">Zhou Maomao</Descriptions.Item>\n  <Descriptions.Item label=\"Telephone\">1810000000</Descriptions.Item>\n  <Descriptions.Item label=\"Live\">Hangzhou, Zhejiang</Descriptions.Item>\n  <Descriptions.Item label=\"Remark\">empty</Descriptions.Item>\n  <Descriptions.Item label=\"Address\">\n    No. 18, Wantang Road, Xihu District, Hangzhou, Zhejiang, China\n  </Descriptions.Item>\n</Descriptions>;\n```",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Descriptions\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| bordered | Whether to display the border | boolean | false |  |\n| colon | Change default props `colon` value of Descriptions.Item | boolean | true |  |\n| column | The number of `DescriptionItems` in a row,could be a number or a object like `{ xs: 8, sm: 16, md: 24}`,(Only set `bordered={true}` to take effect) | number \\| [Record<Breakpoint, number>](https://github.com/@private-basic-components/@private-basic-components/blob/84ca0d23ae52e4f0940f20b0e22eabe743f90dca/components/descriptions/index.tsx#L111C21-L111C56) | 3 |  |\n| contentStyle | Customize content style | CSSProperties | - | 4.10.0 |\n| extra | The action area of the description list, placed at the top-right | ReactNode | - | 4.5.0 |\n| items | Describe the contents of the list item | [DescriptionsItem](#descriptionitem)[] | - | 5.8.0 |\n| labelStyle | Customize label style | CSSProperties | - | 4.10.0 |\n| layout | Define description layout | `horizontal` \\| `vertical` | `horizontal` |  |\n| size | Set the size of the list. Can be set to `middle`,`small`, or not filled | `default` \\| `middle` \\| `small` | - |  |\n| title | The title of the description list, placed at the top | ReactNode | - |  |\n\n### DescriptionItem\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| contentStyle | Customize content style | CSSProperties | - | 4.9.0 |\n| label | The description of the content | ReactNode | - |  |\n| labelStyle | Customize label style | CSSProperties | - | 4.9.0 |\n| span | The number of columns included | number \\| [Screens](/components/grid#col) | 1 | `screens: 5.9.0` |\n\n> The number of span Description.Item. Span={2} takes up the width of two DescriptionItems. When both `style` and `labelStyle`(or `contentStyle`) configured, both of them will work. And next one will overwrite first when conflict."
            },
            "Divider": {
              "description": "- Divide sections of article.\n- Divide inline text and links such as the operation column of table.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| children | The wrapped title | ReactNode | - |  |\n| className | The className of container | string | - |  |\n| dashed | Whether line is dashed | boolean | false |  |\n| orientation | The position of title inside divider | `left` \\| `right` \\| `center` | `center` |  |\n| orientationMargin | The margin-left/right between the title and its closest border, while the `orientation` must be `left` or `right`, If a numeric value of type `string` is provided without a unit, it is assumed to be in pixels (px) by default. | string \\| number | - |  |\n| plain | Divider text show as plain style | boolean | true | 4.2.0 |\n| style | The style object of container | CSSProperties | - |  |\n| type | The direction type of divider | `horizontal` \\| `vertical` | `horizontal` |  |"
            },
            "Drawer": {
              "description": "A Drawer is a panel that is typically overlaid on top of a page and slides in from the side. It contains a set of information or actions. Since the user can interact with the Drawer without leaving the current page, tasks can be achieved more efficiently within the same context.\n\n- Use a Form to create or edit a set of information.\n- Processing subtasks. When subtasks are too heavy for a Popover and we still want to keep the subtasks in the context of the main task, Drawer comes very handy.\n- When the same Form is needed in multiple places.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n**🚨 Note:** v5 use `rootClassName` & `rootStyle` to config wrapper style instead of `className` & `style` in v4 to align the API with Modal.\n\n| Props | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| autoFocus | Whether Drawer should get focused after open | boolean | true | 4.17.0 |\n| afterOpenChange | Callback after the animation ends when switching drawers | function(open) | - |  |\n| className | Config Drawer Panel className. Use `rootClassName` if want to config top dom style | string | - |  |\n| classNames | Config Drawer build-in module's className | `header?: string; body?: string; footer?: string; mask?: string; wrapper?: string;` | - |  |\n| styles | onfig Drawer build-in module's style | `header?: CSSProperties; body?: CSSProperties; footer?: CSSProperties; mask?: CSSProperties; wrapper?: CSSProperties;` | - | 5.10.0 |\n| closeIcon | Custom close icon. 5.7.0: close button will be hidden when setting to `null` or `false` | boolean \\| ReactNode | &lt;CloseOutlined /> |  |\n| contentWrapperStyle | Style of the drawer wrapper of content part | CSSProperties | - |  |\n| destroyOnClose | Whether to unmount child components on closing drawer or not | boolean | false |  |\n| extra | Extra actions area at corner | ReactNode | - | 4.17.0 |\n| footer | The footer for Drawer | ReactNode | - |  |\n| forceRender | Pre-render Drawer component forcibly | boolean | false |  |\n| getContainer | mounted node and display window for Drawer | HTMLElement \\| () => HTMLElement \\| Selectors \\| false | body |  |\n| headerStyle | Style of the drawer header part | CSSProperties | - |  |\n| height | Placement is `top` or `bottom`, height of the Drawer dialog | string \\| number | 378 |  |\n| keyboard | Whether support press esc to close | boolean | true |  |\n| mask | Whether to show mask or not | boolean | true |  |\n| maskClosable | Clicking on the mask (area outside the Drawer) to close the Drawer or not | boolean | true |  |\n| placement | The placement of the Drawer | `top` \\| `right` \\| `bottom` \\| `left` | `right` |  |\n| push | Nested drawers push behavior | boolean \\| { distance: string \\| number } | { distance: 180 } | 4.5.0+ |\n| rootStyle | Style of wrapper element which **contains mask** compare to `style` | CSSProperties | - |  |\n| style | Style of Drawer panel. Use `bodyStyle` if want to config body only | CSSProperties | - |  |\n| size | preset size of drawer, default `378px` and large `736px` | 'default' \\| 'large' | 'default' | 4.17.0 |\n| title | The title for Drawer | ReactNode | - |  |\n| open | Whether the Drawer dialog is visible or not | boolean | false |  |\n| width | Width of the Drawer dialog | string \\| number | 378 |  |\n| zIndex | The `z-index` of the Drawer | number | 1000 |  |\n| onClose | Specify a callback that will be called when a user clicks mask, close button or Cancel button | function(e) | - |  |"
            },
            "Dropdown": {
              "description": "When there are more than a few options to choose from, you can wrap them in a `Dropdown`. By hovering or clicking on the trigger, a dropdown menu will appear, which allows you to choose an option and execute the relevant action.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Dropdown\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| arrow | Whether the dropdown arrow should be visible | boolean \\| { pointAtCenter: boolean } | false |  |\n| autoAdjustOverflow | Whether to adjust dropdown placement automatically when dropdown is off screen | boolean | true | 5.2.0 |\n| autoFocus | Focus element in `overlay` when opened | boolean | false | 4.21.0 |\n| disabled | Whether the dropdown menu is disabled | boolean | - |  |\n| destroyPopupOnHide | Whether destroy dropdown when hidden | boolean | false |  |\n| dropdownRender | Customize dropdown content | (menus: ReactNode) => ReactNode | - | 4.24.0 |\n| getPopupContainer | To set the container of the dropdown menu. The default is to create a div element in body, but you can reset it to the scrolling area and make a relative reposition. [Example on CodePen](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | (triggerNode: HTMLElement) => HTMLElement | () => document.body |  |\n| menu | The menu props | [MenuProps](/components/menu/#api) | - | 4.24.0 |\n| overlayClassName | The class name of the dropdown root element | string | - |  |\n| overlayStyle | The style of the dropdown root element | CSSProperties | - |  |\n| placement | Placement of popup menu: `bottom` `bottomLeft` `bottomRight` `top` `topLeft` `topRight` | string | `bottomLeft` |  |\n| trigger | The trigger mode which executes the dropdown action. Note that hover can't be used on touchscreens | Array&lt;`click`\\|`hover`\\|`contextMenu`> | \\[`hover`] |  |\n| open | Whether the dropdown menu is currently open. Use `visible` under 4.23.0 ([why?](/docs/react/faq#why-open)) | boolean | - | 4.23.0 |\n| onOpenChange | Called when the open state is changed. Not trigger when hidden by click item. Use `onVisibleChange` under 4.23.0 ([why?](/docs/react/faq#why-open)) | (open: boolean) => void | - | 4.23.0 |\n\n### Dropdown.Button\n\nSame props from Dropdown. And includes additional props:\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| buttonsRender | Custom buttons inside Dropdown.Button | (buttons: ReactNode\\[]) => ReactNode\\[] | - |  |\n| loading | Set the loading status of button | boolean \\| { delay: number } | false |  |\n| danger | Set the danger status of button | boolean | - | 4.23.0 |\n| icon | Icon (appears on the right) | ReactNode | - |  |\n| size | Size of the button, the same as [Button](/components/button/#api) | string | `default` |  |\n| type | Type of the button, the same as [Button](/components/button/#api) | string | `default` |  |\n| onClick | The same as [Button](/components/button/#api): called when you click the button on the left | (event) => void | - |  |"
            },
            "Empty": {
              "description": "- When there is no data provided, display for friendly tips.\n- User tutorial to create something in fresh new situation.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n```jsx\n<Empty>\n  <Button>Create</Button>\n</Empty>\n```\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| description | Customize description | ReactNode | - |  |\n| image | Customize image. Will treat as image url when string provided | ReactNode | `Empty.PRESENTED_IMAGE_DEFAULT` |  |\n| imageStyle | The style of image | CSSProperties | - |  |"
            },
            "Flex": {
              "description": "- Good for setting spacing between elements.\n- Suitable for setting various horizontal and vertical alignments.",
              "api": "> This component is available since `@private-basic-components@5.10.0`. The default behavior of Flex in horizontal mode is to align upward, In vertical mode, aligns the stretch, You can adjust this via properties.\n\nCommon props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | type | Default | Version |\n| --- | --- | --- | --- | --- |\n| vertical | Is direction of the flex vertical, use `flex-direction: column` | boolean | `false` |  |\n| wrap | Set whether the element is displayed in a single line or in multiple lines | reference [flex-wrap](https://developer.mozilla.org/en-US/docs/Web/CSS/flex-wrap) | nowrap |  |\n| justify | Sets the alignment of elements in the direction of the main axis | reference [justify-content](https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content) | normal |  |\n| align | Sets the alignment of elements in the direction of the cross axis | reference [align-items](https://developer.mozilla.org/en-US/docs/Web/CSS/align-items) | normal |  |\n| flex | flex CSS shorthand properties | reference [flex](https://developer.mozilla.org/en-US/docs/Web/CSS/flex) | normal |  |\n| gap | Sets the gap between grids | `small` \\| `middle` \\| `large` \\| string \\| number | - |  |\n| component | custom element type | React.ComponentType | `div` |  |"
            },
            "FloatButton": {
              "description": "- For global functionality on the site.\n- Buttons that can be seen wherever you browse.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n> This component is available since `@private-basic-components@5.0.0`.\n\n### common API\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| icon | Set the icon component of button | ReactNode | - |  |\n| description | Text and other | ReactNode | - |  |\n| tooltip | The text shown in the tooltip | ReactNode \\| () => ReactNode |  |  |\n| type | Setting button type | `default` \\| `primary` | `default` |  |\n| shape | Setting button shape | `circle` \\| `square` | `circle` |  |\n| onClick | Set the handler to handle `click` event | (event) => void | - |  |\n| href | The target of hyperlink | string | - |  |\n| target | Specifies where to display the linked URL | string | - |  |\n| badge | Attach Badge to FloatButton. `status` and other props related are not supported. | [BadgeProps](/components/badge#api) | - | 5.4.0 |\n\n### FloatButton.Group\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| shape | Setting button shape of children | `circle` \\| `square` | `circle` |  |\n| trigger | Which action can trigger menu open/close | `click` \\| `hover` | - |  |\n| open | Whether the menu is visible or not, use it with trigger | boolean | - |  |\n| onOpenChange | Callback executed when active menu is changed, use it with trigger | (open: boolean) => void | - |  |\n\n### FloatButton.BackTop\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| duration | Time to return to top(ms) | number | 450 |  |\n| target | Specifies the scrollable area dom node | () => HTMLElement | () => window |  |\n| visibilityHeight | The BackTop button will not show until the scroll height reaches this value | number | 400 |  |\n| onClick | A callback function, which can be executed when you click the button | () => void | - |  |"
            },
            "Image": {
              "description": "- When you need to display pictures.\n- Display when loading a large image or fault tolerant handling when loading fail.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Image\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| alt | Image description | string | - | 4.6.0 |\n| fallback | Load failure fault-tolerant src | string | - | 4.6.0 |\n| height | Image height | string \\| number | - | 4.6.0 |\n| placeholder | Load placeholder, use default placeholder when set `true` | ReactNode | - | 4.6.0 |\n| preview | preview config, disabled when `false` | boolean \\| [PreviewType](#previewtype) | true | 4.6.0 [PreviewType](#previewtype):4.7.0 |\n| src | Image path | string | - | 4.6.0 |\n| width | Image width | string \\| number | - | 4.6.0 |\n| onError | Load failed callback | (event: Event) => void | - | 4.12.0 |\n\nOther attributes [&lt;img>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#Attributes)\n\n### PreviewType\n\n| 参数 | 说明 | 类型 | 默认值 | 版本 |\n| --- | --- | --- | --- | --- |\n| visible | Whether the preview dialog is visible or not | boolean | - | - |\n| src | Custom preview src | string | - | 4.10.0 |\n| getContainer | The mounted node for preview dialog but still display at fullScreen | string \\| HTMLElement \\| (() => HTMLElement) \\| false | - | 4.8.0 |\n| movable | whether can be moved | boolean | true | 5.8.0 |\n| mask | Thumbnail mask | ReactNode | - | 4.9.0 |\n| maskClassName | The className of the mask | string | - | 4.11.0 |\n| rootClassName | The classname of the preview root DOM | string | - | 5.4.0 |\n| scaleStep | `1 + scaleStep` is the step to increase or decrease the scale | number | 0.5 | - |\n| minScale | Min scale | number | 1 | 5.7.0 |\n| maxScale | Max scale | number | 50 | 5.7.0 |\n| closeIcon | Custom close icon | React.ReactNode | - | 5.7.0 |\n| forceRender | Force render preview dialog | boolean | - | - |\n| toolbarRender | Custom toolbar render | (originalNode: React.ReactElement, info: Omit<[ToolbarRenderInfoType](#toolbarrenderinfotype), 'current' \\| 'total'>) => React.ReactNode | - | 5.7.0 |\n| imageRender | Custom preview content | (originalNode: React.ReactElement, info: { transform: [TransformType](#transformtype) }) => React.ReactNode | - | 5.7.0 |\n| onTransform | Callback when the transform of image changed | { transform: [TransformType](#transformtype), action: [TransformAction](#transformaction) } | - | 5.7.0 |\n| onVisibleChange | Callback when `visible` changed | (visible: boolean, prevVisible: boolean) => void | - | - |"
            },
            "Input": {
              "description": "- A user input in a form field is needed.\n- A search input is required.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Input\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| addonAfter | The label text displayed after (on the right side of) the input field | ReactNode | - |  |\n| addonBefore | The label text displayed before (on the left side of) the input field | ReactNode | - |  |\n| allowClear | If allow to remove input content with clear icon | boolean \\| { clearIcon: ReactNode } | false |  |\n| bordered | Whether has border style | boolean | true | 4.5.0 |\n| classNames | Semantic DOM class | Record<[SemanticDOM](#input-1), string> | - | 5.4.0 |\n| defaultValue | The initial input content | string | - |  |\n| disabled | Whether the input is disabled | boolean | false |  |\n| id | The ID for input | string | - |  |\n| maxLength | The maximum number of characters in Input | number | - |  |\n| showCount | Whether to show character count | boolean \\| { formatter: (info: { value: string, count: number, maxLength?: number }) => ReactNode } | false | 4.18.0 info.value: 4.23.0 |\n| status | Set validation status | 'error' \\| 'warning' | - | 4.19.0 |\n| styles | Semantic DOM style | Record<[SemanticDOM](#input-1), CSSProperties> | - | 5.4.0 |\n| prefix | The prefix icon for the Input | ReactNode | - |  |\n| size | The size of the input box. Note: in the context of a form, the `middle` size is used | `large` \\| `middle` \\| `small` | - |  |\n| suffix | The suffix icon for the Input | ReactNode | - |  |\n| type | The type of input, see: [MDN](https://developer.mozilla.org/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types)( use `Input.TextArea` instead of `type=\"textarea\"`) | string | `text` |  |\n| value | The input content value | string | - |  |\n| onChange | Callback when user input | function(e) | - |  |\n| onPressEnter | The callback function that is triggered when Enter key is pressed | function(e) | - |  |\n\n> When `Input` is used in a `Form.Item` context, if the `Form.Item` has the `id` props defined then `value`, `defaultValue`, and `id` props of `Input` are automatically set.\n\nThe rest of the props of Input are exactly the same as the original [input](https://reactjs.org/docs/dom-elements.html#all-supported-html-attributes).\n\n### Input.TextArea\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| allowClear | Customize clear icon | boolean \\| { clearIcon?: ReactNode } | false | 5.8.0: Support object type |\n| autoSize | Height autosize feature, can be set to true \\| false or an object { minRows: 2, maxRows: 6 } | boolean \\| object | false |  |\n| bordered | Whether has border style | boolean | true | 4.5.0 |\n| classNames | Semantic DOM class | Record<[SemanticDOM](#inputtextarea-1), string> | - | 5.4.0 |\n| defaultValue | The initial input content | string | - |  |\n| maxLength | The maximum number of characters in TextArea | number | - | 4.7.0 |\n| showCount | Whether to show character count | boolean \\| { formatter: (info: { value: string, count: number, maxLength?: number }) => string } | false | 4.7.0 formatter: 4.10.0 info.value: 4.23.0 |\n| styles | Semantic DOM style | Record<[SemanticDOM](#inputtextarea-1), CSSProperties> | - | 5.4.0 |\n| value | The input content value | string | - |  |\n| onPressEnter | The callback function that is triggered when Enter key is pressed | function(e) | - |  |\n| onResize | The callback function that is triggered when resize | function({ width, height }) | - |  |\n\nThe rest of the props of `Input.TextArea` are the same as the original [textarea](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea).\n\n#### Input.Search\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| enterButton | Whether to show an enter button after input. This property conflicts with the `addonAfter` property | boolean \\| ReactNode | false |\n| loading | Search box with loading | boolean | false |\n| onSearch | The callback function triggered when you click on the search-icon, the clear-icon or press the Enter key | function(value, event, { source: \"input\" \\| \"clear\" }) | - |\n\nSupports all props of `Input`.\n\n#### Input.Password\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| iconRender | Custom toggle button | (visible) => ReactNode | (visible) => (visible ? &lt;EyeOutlined /> : &lt;EyeInvisibleOutlined />) | 4.3.0 |\n| visibilityToggle | Whether show toggle button or control password visible | boolean \\| [VisibilityToggle](#visibilitytoggle) | true |  |\n\n#### VisibilityToggle\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| visible | Whether the password is show or hide | boolean | false | 4.24.0 |\n| onVisibleChange | Callback executed when visibility of the password is changed | boolean | - | 4.24.0 |\n\n#### Input Methods\n\n| Name | Description | Parameters | Version |\n| --- | --- | --- | --- |\n| blur | Remove focus | - |  |\n| focus | Get focus | (option?: { preventScroll?: boolean, cursor?: 'start' \\| 'end' \\| 'all' }) | option - 4.10.0 |\n\n### Semantic DOM\n\n#### Input\n\n<!-- prettier-ignore -->\n| Property | Description | Version |\n| --- | --- | --- |\n| input | `input` element | 5.4.0 |\n| prefix | Wrapper of prefix | 5.4.0 |\n| suffix | Wrapper of suffix | 5.4.0 |\n| count | Text count element | 5.4.0 |\n\n#### Input.TextArea\n\n<!-- prettier-ignore -->\n| Property | Description | Version |\n| --- | --- | --- |\n| textarea | `textarea` element | 5.4.0 |\n| count | Text count element | 5.4.0 |"
            },
            "InputNumber": {
              "description": "When a numeric value needs to be provided.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| addonAfter | The label text displayed after (on the right side of) the input field | ReactNode | - |  |\n| addonBefore | The label text displayed before (on the left side of) the input field | ReactNode | - |  |\n| autoFocus | If get focus when component mounted | boolean | false | - |\n| bordered | Whether has border style | boolean | true | 4.12.0 |\n| controls | Whether to show `+-` controls, or set custom arrows icon | boolean \\| { upIcon?: React.ReactNode; downIcon?: React.ReactNode; } | - | 4.19.0 |\n| decimalSeparator | Decimal separator | string | - | - |\n| defaultValue | The initial value | number | - | - |\n| disabled | If disable the input | boolean | false | - |\n| formatter | Specifies the format of the value presented | function(value: number \\| string, info: { userTyping: boolean, input: string }): string | - | info: 4.17.0 |\n| keyboard | If enable keyboard behavior | boolean | true | 4.12.0 |\n| max | The max value | number | [Number.MAX_SAFE_INTEGER](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER) | - |\n| min | The min value | number | [Number.MIN_SAFE_INTEGER](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER) | - |\n| parser | Specifies the value extracted from formatter | function(string): number | - | - |\n| precision | The precision of input value. Will use `formatter` when config of `formatter` | number | - | - |\n| readOnly | If readonly the input | boolean | false | - |\n| status | Set validation status | 'error' \\| 'warning' | - | 4.19.0 |\n| prefix | The prefix icon for the Input | ReactNode | - | 4.17.0 |\n| size | The height of input box | `large` \\| `middle` \\| `small` | - | - |\n| step | The number to which the current value is increased or decreased. It can be an integer or decimal | number \\| string | 1 | - |\n| stringMode | Set value as string to support high precision decimals. Will return string value by `onChange` | boolean | false | 4.13.0 |\n| value | The current value | number | - | - |\n| onChange | The callback triggered when the value is changed | function(value: number \\| string \\| null) | - | - |\n| onPressEnter | The callback function that is triggered when Enter key is pressed | function(e) | - | - |\n| onStep | The callback function that is triggered when click up or down buttons | (value: number, info: { offset: number, type: 'up' \\| 'down' }) => void | - | 4.7.0 |"
            },
            "List": {
              "description": "A list can be used to display content related to a single subject. The content can consist of multiple elements of varying type and size.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### List\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| bordered | Toggles rendering of the border around the list | boolean | false |  |\n| dataSource | DataSource array for list | any\\[] | - |  |\n| footer | List footer renderer | ReactNode | - |  |\n| grid | The grid type of list. You can set grid to something like {gutter: 16, column: 4} | [object](#list-grid-props) | - |  |\n| header | List header renderer | ReactNode | - |  |\n| itemLayout | The layout of list | `horizontal` \\| `vertical` | `horizontal` |  |\n| loading | Shows a loading indicator while the contents of the list are being fetched | boolean \\| [SpinProps](/components/spin/#api) ([more](https://github.com/@private-basic-components/@private-basic-components/issues/8659)) | false |  |\n| loadMore | Shows a load more content | ReactNode | - |  |\n| locale | The i18n text including empty text | object | {emptyText: `No Data`} |  |\n| pagination | Pagination [config](/components/pagination/), hide it by setting it to false | boolean \\| object | false |  |\n| renderItem | Customize list item when using `dataSource` | (item) => ReactNode | - |  |\n| rowKey | Item's unique value, could be an Item's key which holds a unique value of type `React.Key` or function that receives Item and returns a `React.Key` | `keyof` T \\| (item: T) => `React.Key` | `\"key\"` |  |\n| size | Size of list | `default` \\| `large` \\| `small` | `default` |  |\n| split | Toggles rendering of the split under the list item | boolean | true |  |\n\n### pagination\n\nProperties for pagination.\n\n| Property | Description                               | Type                         | Default  |\n| -------- | ----------------------------------------- | ---------------------------- | -------- |\n| position | The specify the position of `Pagination`  | `top` \\| `bottom` \\| `both`  | `bottom` |\n| align    | The specify the alignment of `Pagination` | `start` \\| `center` \\| `end` | `end`    |\n\nMore about pagination, please check [`Pagination`](/components/pagination/).\n\n### List grid props\n\n| Property | Description              | Type   | Default | Version |\n| -------- | ------------------------ | ------ | ------- | ------- |\n| column   | The column of grid       | number | -       |         |\n| gutter   | The spacing between grid | number | 0       |         |\n| xs       | `<576px` column of grid  | number | -       |         |\n| sm       | `≥576px` column of grid  | number | -       |         |\n| md       | `≥768px` column of grid  | number | -       |         |\n| lg       | `≥992px` column of grid  | number | -       |         |\n| xl       | `≥1200px` column of grid | number | -       |         |\n| xxl      | `≥1600px` column of grid | number | -       |         |\n\n### List.Item\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| actions | The actions content of list item. If `itemLayout` is `vertical`, shows the content on bottom, otherwise shows content on the far right | Array&lt;ReactNode> | - |  |\n| extra | The extra content of list item. If `itemLayout` is `vertical`, shows the content on right, otherwise shows content on the far right | ReactNode | - |  |\n\n### List.Item.Meta\n\n| Property    | Description                  | Type      | Default | Version |\n| ----------- | ---------------------------- | --------- | ------- | ------- |\n| avatar      | The avatar of list item      | ReactNode | -       |         |\n| description | The description of list item | ReactNode | -       |         |\n| title       | The title of list item       | ReactNode | -       |         |"
            },
            "Mentions": {
              "description": "When you need to mention someone or something.\n\n### Usage upgrade after 5.1.0\n\n<Alert message=\"After version 5.1.0, we provide a simpler usage &lt;Mentions options={[...]} /&gt;  with better performance and potential of writing simpler code style in your applications. Meanwhile, we deprecated the old usage in browser console, we will remove it in @private-basic-components 6.0.\"></Alert>\n\n```jsx\n// works when >=5.1.0, recommended ✅\nconst options = [{ value: 'sample', label: 'sample' }];\nreturn <Mentions options={options} />;\n\n// works when <5.1.0, deprecated when >=5.1.0 🙅🏻‍♀️\nreturn (\n  <Mentions onChange={onChange}>\n    <Mentions.Option value=\"sample\">Sample</Mentions.Option>\n  </Mentions>\n);\n```",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Mention\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| autoFocus | Auto get focus when component mounted | boolean | false |  |\n| autoSize | Textarea height autosize feature, can be set to true \\| false or an object { minRows: 2, maxRows: 6 } | boolean \\| object | false |  |\n| defaultValue | Default value | string | - |  |\n| filterOption | Customize filter option logic | false \\| (input: string, option: OptionProps) => boolean | - |  |\n| getPopupContainer | Set the mount HTML node for suggestions | () => HTMLElement | - |  |\n| notFoundContent | Set mentions content when not match | ReactNode | `Not Found` |  |\n| placement | Set popup placement | `top` \\| `bottom` | `bottom` |  |\n| prefix | Set trigger prefix keyword | string \\| string\\[] | `@` |  |\n| split | Set split string before and after selected mention | string | ` ` |  |\n| status | Set validation status | 'error' \\| 'warning' \\| 'success' \\| 'validating' | - | 4.19.0 |\n| validateSearch | Customize trigger search logic | (text: string, props: MentionsProps) => void | - |  |\n| value | Set value of mentions | string | - |  |\n| onBlur | Trigger when mentions lose focus | () => void | - |  |\n| onChange | Trigger when value changed | (text: string) => void | - |  |\n| onFocus | Trigger when mentions get focus | () => void | - |  |\n| onResize | The callback function that is triggered when textarea resize | function({ width, height }) | - |  |\n| onSearch | Trigger when prefix hit | (text: string, prefix: string) => void | - |  |\n| onSelect | Trigger when user select the option | (option: OptionProps, prefix: string) => void | - |  |\n| options | Option Configuration | [Options](#option) | \\[] | 5.1.0 |\n\n### Mention methods\n\n| Name    | Description  |\n| ------- | ------------ |\n| blur()  | Remove focus |\n| focus() | Get focus    |\n\n### Option\n\n<!-- prettier-ignore -->\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| label | Title of the option | React.ReactNode | - |\n| key | The key value of the option | string | - |\n| disabled | Optional | boolean | - |\n| className | className | string | - |\n| style | The style of the option | React.CSSProperties | - |"
            },
            "Menu": {
              "description": "Navigation is an important part of any website, as a good navigation setup allows users to move around the site quickly and efficiently. @private-basic-components offers two navigation options: top and side. Top navigation provides all the categories and functions of the website. Side navigation provides the multi-level structure of the website.\n\nMore layouts with navigation: [Layout](/components/layout).",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Menu\n\n| Param | Description | Type | Default value | Version |\n| --- | --- | --- | --- | --- |\n| defaultOpenKeys | Array with the keys of default opened sub menus | string\\[] | - |  |\n| defaultSelectedKeys | Array with the keys of default selected menu items | string\\[] | - |  |\n| expandIcon | custom expand icon of submenu | ReactNode \\| `(props: SubMenuProps & { isSubMenu: boolean }) => ReactNode` | - | 4.9.0 |\n| forceSubMenuRender | Render submenu into DOM before it becomes visible | boolean | false |  |\n| inlineCollapsed | Specifies the collapsed status when menu is inline mode | boolean | - |  |\n| inlineIndent | Indent (in pixels) of inline menu items on each level | number | 24 |  |\n| items | Menu item content | [ItemType\\[\\]](#itemtype) | - | 4.20.0 |\n| mode | Type of menu | `vertical` \\| `horizontal` \\| `inline` | `vertical` |  |\n| multiple | Allows selection of multiple items | boolean | false |  |\n| openKeys | Array with the keys of currently opened sub-menus | string\\[] | - |  |\n| overflowedIndicator | Customized the ellipsis icon when menu is collapsed horizontally | ReactNode | `<EllipsisOutlined />` |  |\n| selectable | Allows selecting menu items | boolean | true |  |\n| selectedKeys | Array with the keys of currently selected menu items | string\\[] | - |  |\n| style | Style of the root node | CSSProperties | - |  |\n| subMenuCloseDelay | Delay time to hide submenu when mouse leaves (in seconds) | number | 0.1 |  |\n| subMenuOpenDelay | Delay time to show submenu when mouse enters, (in seconds) | number | 0 |  |\n| theme | Color theme of the menu | `light` \\| `dark` | `light` |  |\n| triggerSubMenuAction | Which action can trigger submenu open/close | `hover` \\| `click` | `hover` |  |\n| onClick | Called when a menu item is clicked | function({ item, key, keyPath, domEvent }) | - |  |\n| onDeselect | Called when a menu item is deselected (multiple mode only) | function({ item, key, keyPath, selectedKeys, domEvent }) | - |  |\n| onOpenChange | Called when sub-menus are opened or closed | function(openKeys: string\\[]) | - |  |\n| onSelect | Called when a menu item is selected | function({ item, key, keyPath, selectedKeys, domEvent }) | - |  |\n\n> More options in [rc-menu](https://github.com/react-component/menu#api)\n\n### ItemType\n\n> type ItemType = [MenuItemType](#MenuItemType) | [SubMenuType](#SubMenuType) | [MenuItemGroupType](#MenuItemGroupType) | [MenuDividerType](#MenuDividerType);\n\n#### MenuItemType\n\n| Param    | Description                          | Type      | Default value | Version |\n| -------- | ------------------------------------ | --------- | ------------- | ------- |\n| danger   | Display the danger style             | boolean   | false         |         |\n| disabled | Whether menu item is disabled        | boolean   | false         |         |\n| icon     | The icon of the menu item            | ReactNode | -             |         |\n| key      | Unique ID of the menu item           | string    | -             |         |\n| label    | Menu label                           | ReactNode | -             |         |\n| title    | Set display title for collapsed item | string    | -             |         |\n\n#### SubMenuType\n\n<!-- prettier-ignore -->\n| Property | Description | Type | Default value | Version |\n| --- | --- | --- | --- | --- |\n| children | Sub-menus or sub-menu items | [ItemType\\[\\]](#itemtype) | - |  |\n| disabled | Whether sub-menu is disabled | boolean | false |  |\n| icon | Icon of sub menu | ReactNode | - |  |\n| key | Unique ID of the sub-menu | string | - |  |\n| label | Menu label | ReactNode | - |  |\n| popupClassName | Sub-menu class name, not working when `mode=\"inline\"` | string | - |  |\n| popupOffset | Sub-menu offset, not working when `mode=\"inline\"` | \\[number, number] | - |  |\n| theme | Color theme of the SubMenu (inherits from Menu by default) |  | `light` \\| `dark` | - |  |\n| onTitleClick | Callback executed when the sub-menu title is clicked | function({ key, domEvent }) | - |  |\n\n#### MenuItemGroupType\n\nDefine `type` as `group` to make as group:\n\n```ts\nconst groupItem = {\n  type: 'group', // Must have\n  label: 'My Group',\n  children: [],\n};\n```\n\n| Param    | Description            | Type                              | Default value | Version |\n| -------- | ---------------------- | --------------------------------- | ------------- | ------- |\n| children | Sub-menu items         | [MenuItemType\\[\\]](#menuitemtype) | -             |         |\n| label    | The title of the group | ReactNode                         | -             |         |\n\n#### MenuDividerType\n\nDivider line in between menu items, only used in vertical popup Menu or Dropdown Menu. Need define the `type` as `divider`:\n\n```ts\nconst dividerItem = {\n  type: 'divider', // Must have\n};\n```\n\n| Param  | Description            | Type    | Default value | Version |\n| ------ | ---------------------- | ------- | ------------- | ------- |\n| dashed | Whether line is dashed | boolean | false         |         |"
            },
            "Message": {
              "description": "- To provide feedback such as success, warning, error etc.\n- A message is displayed at top and center and will be dismissed automatically, as a non-interrupting light-weighted prompt.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\nThis component provides some static methods, with usage and arguments as following:\n\n- `message.success(content, [duration], onClose)`\n- `message.error(content, [duration], onClose)`\n- `message.info(content, [duration], onClose)`\n- `message.warning(content, [duration], onClose)`\n- `message.loading(content, [duration], onClose)`\n\n| Argument | Description | Type | Default |\n| --- | --- | --- | --- |\n| content | The content of the message | ReactNode \\| config | - |\n| duration | Time(seconds) before auto-dismiss, don't dismiss if set to 0 | number | 1.5 |\n| onClose | Specify a function that will be called when the message is closed | function | - |\n\n`afterClose` can be called in thenable interface:\n\n- `message[level](content, [duration]).then(afterClose)`\n- `message[level](content, [duration], onClose).then(afterClose)`\n\nwhere `level` refers one static methods of `message`. The result of `then` method will be a Promise.\n\nSupports passing parameters wrapped in an object:\n\n- `message.open(config)`\n- `message.success(config)`\n- `message.error(config)`\n- `message.info(config)`\n- `message.warning(config)`\n- `message.loading(config)`\n\nThe properties of config are as follows:\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| className | Customized CSS class | string | - |\n| content | The content of the message | ReactNode | - |\n| duration | Time(seconds) before auto-dismiss, don't dismiss if set to 0 | number | 3 |\n| icon | Customized Icon | ReactNode | - |\n| key | The unique identifier of the Message | string \\| number | - |\n| style | Customized inline style | [CSSProperties](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e434515761b36830c3e58a970abf5186f005adac/types/react/index.d.ts#L794) | - |\n| onClick | Specify a function that will be called when the message is clicked | function | - |\n| onClose | Specify a function that will be called when the message is closed | function | - |\n\n### Global static methods\n\nMethods for global configuration and destruction are also provided:\n\n- `message.config(options)`\n- `message.destroy()`\n\n> use `message.destroy(key)` to remove a message。\n\n#### message.config\n\n> When you use `ConfigProvider` for global configuration, the system will automatically start RTL mode by default.(4.3.0+)\n>\n> When you want to use it alone, you can start the RTL mode through the following settings.\n\n```js\nmessage.config({\n  top: 100,\n  duration: 2,\n  maxCount: 3,\n  rtl: true,\n  prefixCls: 'my-message',\n});\n```\n\n| Argument | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| duration | Time before auto-dismiss, in seconds | number | 3 |  |\n| getContainer | Return the mount node for Message, but still display at fullScreen | () => HTMLElement | () => document.body |  |\n| maxCount | Max message show, drop oldest if exceed limit | number | - |  |\n| prefixCls | The prefix className of message node | string | `ant-message` | 4.5.0 |\n| rtl | Whether to enable RTL mode | boolean | false |  |\n| top | Distance from top | number | 8 |  |"
            },
            "Modal": {
              "description": "When requiring users to interact with the application, but without jumping to a new page and interrupting the user's workflow, you can use `Modal` to create a new floating layer over the current page to get user feedback or display information.\n\nAdditionally, if you need show a simple confirmation dialog, you can use [`App.useApp`](/components/app/) hooks.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| afterClose | Specify a function that will be called when modal is closed completely | function | - |  |\n| classNames | Config Modal build-in module's className | `header?: string; body?: string; footer?: string; mask?: string; wrapper?: string;` | - |  |\n| styles | Config Modal build-in module's style | `header?: CSSProperties; body?: CSSProperties; footer?: CSSProperties; mask?: CSSProperties; wrapper?: CSSProperties;` | - | 5.10.0 |\n| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#api) | - |  |\n| cancelText | Text of the Cancel button | ReactNode | `Cancel` |  |\n| centered | Centered Modal | boolean | false |  |\n| closeIcon | Custom close icon. 5.7.0: close button will be hidden when setting to `null` or `false` | boolean \\| ReactNode | &lt;CloseOutlined /> |  |\n| confirmLoading | Whether to apply loading visual effect for OK button or not | boolean | false |  |\n| destroyOnClose | Whether to unmount child components on onClose | boolean | false |  |\n| focusTriggerAfterClose | Whether need to focus trigger element after dialog is closed | boolean | true | 4.9.0 |\n| footer | Footer content, set as `footer={null}` when you don't need default buttons | (params:[footerRenderParams](/components/modal-cn#footerrenderparams))=> React.ReactNode \\| React.ReactNode | (OK and Cancel buttons) |  |\n| forceRender | Force render Modal | boolean | false |  |\n| getContainer | The mounted node for Modal but still display at fullscreen | HTMLElement \\| () => HTMLElement \\| Selectors \\| false | document.body |  |\n| keyboard | Whether support press esc to close | boolean | true |  |\n| mask | Whether show mask or not | boolean | true |  |\n| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | true |  |\n| modalRender | Custom modal content render | (node: ReactNode) => ReactNode | - | 4.7.0 |\n| okButtonProps | The ok button props | [ButtonProps](/components/button/#api) | - |  |\n| okText | Text of the OK button | ReactNode | `OK` |  |\n| okType | Button `type` of the OK button | string | `primary` |  |\n| style | Style of floating layer, typically used at least for adjusting the position | CSSProperties | - |  |\n| title | The modal dialog's title | ReactNode | - |  |\n| open | Whether the modal dialog is visible or not | boolean | false |  |\n| width | Width of the modal dialog | string \\| number | 520 |  |\n| wrapClassName | The class name of the container of the modal dialog | string | - |  |\n| zIndex | The `z-index` of the Modal | number | 1000 |  |\n| onCancel | Specify a function that will be called when a user clicks mask, close button on top right or Cancel button | function(e) | - |  |\n| onOk | Specify a function that will be called when a user clicks the OK button | function(e) | - |  |\n| afterOpenChange | Callback when the animation ends when Modal is turned on and off | (open: boolean) => void | - | 5.4.0 |\n\n#### Note\n\n- The state of Modal will be preserved at it's component lifecycle by default, if you wish to open it with a brand new state every time, set `destroyOnClose` on it.\n- There is a situation that using `<Modal />` with Form, which won't clear fields value when closing Modal even you have set `destroyOnClose`. You need `<Form preserve={false} />` in this case.\n- `Modal.method()` RTL mode only supports hooks.\n\n### Modal.method()\n\nThere are five ways to display the information based on the content's nature:\n\n- `Modal.info`\n- `Modal.success`\n- `Modal.error`\n- `Modal.warning`\n- `Modal.confirm`\n\nThe items listed above are all functions, expecting a settings object as parameter. The properties of the object are follows:\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| afterClose | Specify a function that will be called when modal is closed completely | function | - | 4.9.0 |\n| autoFocusButton | Specify which button to autofocus | null \\| `ok` \\| `cancel` | `ok` |  |\n| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#api) | - |  |\n| cancelText | Text of the Cancel button with Modal.confirm | string | `Cancel` |  |\n| centered | Centered Modal | boolean | false |  |\n| className | The className of container | string | - |  |\n| closeIcon | Custom close icon. 5.7.0: close button will be hidden when setting to `null` or `false` | boolean \\| ReactNode | &lt;CloseOutlined /> |  |\n| content | Content | ReactNode | - |  |\n| footer | Footer content, set as `footer: null` when you don't need default buttons | (params:[footerRenderParams](/components/modal-cn#footerrenderparams))=> React.ReactNode \\| React.ReactNode | - | 5.9.0 |\n| getContainer | Return the mount node for Modal | HTMLElement \\| () => HTMLElement \\| Selectors \\| false | document.body |  |\n| icon | Custom icon | ReactNode | &lt;ExclamationCircleFilled /> |  |\n| keyboard | Whether support press esc to close | boolean | true |  |\n| mask | Whether show mask or not. | boolean | true |  |\n| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | false |  |\n| okButtonProps | The ok button props | [ButtonProps](/components/button/#api) | - |  |\n| okText | Text of the OK button | string | `OK` |  |\n| okType | Button `type` of the OK button | string | `primary` |  |\n| style | Style of floating layer, typically used at least for adjusting the position | CSSProperties | - |  |\n| title | Title | ReactNode | - |  |\n| width | Width of the modal dialog | string \\| number | 416 |  |\n| wrapClassName | The class name of the container of the modal dialog | string | - | 4.18.0 |\n| zIndex | The `z-index` of the Modal | number | 1000 |  |\n| onCancel | Specify a function that will be called when the user clicks the Cancel button. The parameter of this function is a function whose execution should include closing the dialog. If the function does not take any parameter (`!onCancel.length`) then modal dialog will be closed unless returned value is `true` (`!!onCancel()`). You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - |  |\n| onOk | Specify a function that will be called when the user clicks the OK button. The parameter of this function is a function whose execution should include closing the dialog. If the function does not take any parameter (`!onOk.length`) then modal dialog will be closed unless returned value is `true` (`!!onOk()`). You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - |  |\n\nAll the `Modal.method`s will return a reference, and then we can update and close the modal dialog by the reference.\n\n```jsx\nconst modal = Modal.info();\n\nmodal.update({\n  title: 'Updated title',\n  content: 'Updated content',\n});\n\n// on 4.8.0 or above, you can pass a function to update modal\nmodal.update((prevConfig) => ({\n  ...prevConfig,\n  title: `${prevConfig.title} (New)`,\n}));\n\nmodal.destroy();\n```\n\n- `Modal.destroyAll`\n\n`Modal.destroyAll()` could destroy all confirmation modal dialogs(`Modal.confirm|success|info|error|warning`). Usually, you can use it in router change event to destroy confirm modal dialog automatically without use modal reference to close( it's too complex to use for all modal dialogs)\n\n```jsx\nimport { browserHistory } from 'react-router';\n\n// router change\nbrowserHistory.listen(() => {\n  Modal.destroyAll();\n});\n```\n\n### Modal.useModal()\n\nWhen you need using Context, you can use `contextHolder` which created by `Modal.useModal` to insert into children. Modal created by hooks will get all the context where `contextHolder` are. Created `modal` has the same creating function with `Modal.method`.\n\n```jsx\nconst [modal, contextHolder] = Modal.useModal();\n\nReact.useEffect(() => {\n  modal.confirm({\n    // ...\n  });\n}, []);\n\nreturn <div>{contextHolder}</div>;\n```\n\n`modal.confirm` return method:\n\n- `destroy`: Destroy current modal\n- `update`: Update current modal\n- `then`: (Hooks only) Promise chain call, support `await` operation\n\n```tsx\n// Return `true` when click `onOk` and `false` when click `onCancel`\nconst confirmed = await modal.confirm({ ... });\n```"
            },
            "Notification": {
              "description": "To display a notification message at any of the four corners of the viewport. Typically it can be used in the following cases:\n\n- A notification with complex content.\n- A notification providing a feedback based on the user interaction. Or it may show some details about upcoming steps the user may have to follow.\n- A notification that is pushed by the application.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n- `notification.success(config)`\n- `notification.error(config)`\n- `notification.info(config)`\n- `notification.warning(config)`\n- `notification.open(config)`\n- `notification.destroy(key?: String)`\n\nThe properties of config are as follows:\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| btn | Customized close button | ReactNode | - | - |\n| className | Customized CSS class | string | - | - |\n| closeIcon | Custom close icon | boolean \\| ReactNode | true | 5.7.0: close button will be hidden when setting to null or false |\n| description | The content of notification box (required) | ReactNode | - | - |\n| duration | Time in seconds before Notification is closed. When set to 0 or null, it will never be closed automatically | number | 4.5 | - |\n| icon | Customized icon | ReactNode | - | - |\n| key | The unique identifier of the Notification | string | - | - |\n| message | The title of notification box (required) | ReactNode | - | - |\n| placement | Position of Notification, can be one of `topLeft` `topRight` `bottomLeft` `bottomRight` | string | `topRight` | - |\n| style | Customized inline style | [CSSProperties](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/e434515761b36830c3e58a970abf5186f005adac/types/react/index.d.ts#L794) | - | - |\n| role | The semantics of notification content recognized by screen readers. The default value is `alert`. When set as the default value, the screen reader will promptly interrupt any ongoing content reading and prioritize the notification content for immediate attention. | `alert \\| status` | `alert` | 5.6.0 |\n| onClick | Specify a function that will be called when the notification is clicked | function | - | - |\n| onClose | Trigger when notification closed | function | - | - |\n| props | An object that can contain `data-*`, `aria-*`, or `role` props, to be put on the notification `div`. This currently only allows `data-testid` instead of `data-*` in TypeScript. See https://github.com/microsoft/TypeScript/issues/28960. | Object | - | - |\n\n- `notification.useNotification(config)`\n\nThe properties of config are as follows:\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| bottom | Distance from the bottom of the viewport, when `placement` is `bottomRight` or `bottomLeft` (unit: pixels) | number | 24 |  |\n| closeIcon | Custom close icon | boolean \\| ReactNode | true | 5.7.0: close button will be hidden when setting to null or false |\n| getContainer | Return the mount node for Notification | () => HTMLNode | () => document.body |  |\n| placement | Position of Notification, can be one of `topLeft` `topRight` `bottomLeft` `bottomRight` | string | `topRight` |  |\n| rtl | Whether to enable RTL mode | boolean | false |  |\n| stack | Notifications will be stacked when amount is over threshold | boolean \\| `{ threshold: number }` | `{ threshold: 3 }` | 5.10.0 |\n| top | Distance from the top of the viewport, when `placement` is `topRight` or `topLeft` (unit: pixels) | number | 24 |  |\n| maxCount | Max Notification show, drop oldest if exceed limit | number | - | 4.17.0 |\n\n`notification` also provides a global `config()` method that can be used for specifying the default options. Once this method is used, all the notification boxes will take into account these globally defined options when displaying.\n\n### Global configuration\n\n`notification.config(options)`\n\n> When you use `ConfigProvider` for global configuration, the system will automatically start RTL mode by default.(4.3.0+)\n>\n> When you want to use it alone, you can start the RTL mode through the following settings.\n\n#### notification.config\n\n```js\nnotification.config({\n  placement: 'bottomRight',\n  bottom: 50,\n  duration: 3,\n  rtl: true,\n});\n```\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| bottom | Distance from the bottom of the viewport, when `placement` is `bottomRight` or `bottomLeft` (unit: pixels) | number | 24 |  |\n| closeIcon | Custom close icon | boolean \\| ReactNode | true | 5.7.0: close button will be hidden when setting to null or false |\n| duration | Time in seconds before Notification is closed. When set to 0 or null, it will never be closed automatically | number | 4.5 |  |\n| getContainer | Return the mount node for Notification, but still display at fullScreen | () => HTMLNode | () => document.body |  |\n| placement | Position of Notification, can be one of `topLeft` `topRight` `bottomLeft` `bottomRight` | string | `topRight` |  |\n| rtl | Whether to enable RTL mode | boolean | false |  |\n| top | Distance from the top of the viewport, when `placement` is `topRight` or `topLeft` (unit: pixels) | number | 24 |  |\n| maxCount | Max Notification show, drop oldest if exceed limit | number | - | 4.17.0 |"
            },
            "Pagination": {
              "description": "- When it will take a long time to load/render all items.\n- If you want to browse the data by navigating through pages.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n```jsx\n<Pagination onChange={onChange} total={50} />\n```\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| current | Current page number | number | - |  |\n| defaultCurrent | Default initial page number | number | 1 |  |\n| defaultPageSize | Default number of data items per page | number | 10 |  |\n| disabled | Disable pagination | boolean | - |  |\n| hideOnSinglePage | Whether to hide pager on single page | boolean | false |  |\n| itemRender | To customize item's innerHTML | (page, type: 'page' \\| 'prev' \\| 'next', originalElement) => React.ReactNode | - |  |\n| pageSize | Number of data items per page | number | - |  |\n| pageSizeOptions | Specify the sizeChanger options | string\\[] \\| number\\[] | \\[`10`, `20`, `50`, `100`] |  |\n| responsive | If `size` is not specified, `Pagination` would resize according to the width of the window | boolean | - |  |\n| showLessItems | Show less page items | boolean | false |  |\n| showQuickJumper | Determine whether you can jump to pages directly | boolean \\| { goButton: ReactNode } | false |  |\n| showSizeChanger | Determine whether to show `pageSize` select, it will be true when `total > 50` | boolean | - |  |\n| showTitle | Show page item's title | boolean | true |  |\n| showTotal | To display the total number and range | function(total, range) | - |  |\n| simple | Whether to use simple mode | boolean | - |  |\n| size | Specify the size of `Pagination`, can be set to `small` | `default` \\| `small` | `default` |  |\n| total | Total number of data items | number | 0 |  |\n| onChange | Called when the page number or `pageSize` is changed, and it takes the resulting page number and pageSize as its arguments | function(page, pageSize) | - |  |\n| onShowSizeChange | Called when `pageSize` is changed | function(current, size) | - |  |"
            },
            "Popconfirm": {
              "description": "A simple and compact dialog used for asking for user confirmation.\n\nThe difference with the `confirm` modal dialog is that it's more lightweight than the static popped full-screen confirm modal.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Param | Description | Type | Default value | Version |\n| --- | --- | --- | --- | --- |\n| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#api) | - |  |\n| cancelText | The text of the Cancel button | string | `Cancel` |  |\n| disabled | Whether show popconfirm when click its childrenNode | boolean | false |  |\n| icon | Customize icon of confirmation | ReactNode | &lt;ExclamationCircle /> |  |\n| okButtonProps | The ok button props | [ButtonProps](/components/button/#api) | - |  |\n| okText | The text of the Confirm button | string | `OK` |  |\n| okType | Button `type` of the Confirm button | string | `primary` |  |\n| showCancel | Show cancel button | boolean | true | 4.18.0 |\n| title | The title of the confirmation box | ReactNode \\| () => ReactNode | - |  |\n| description | The description of the confirmation box title | ReactNode \\| () => ReactNode | - | 5.1.0 |\n| onCancel | A callback of cancel | function(e) | - |  |\n| onConfirm | A callback of confirmation | function(e) | - |  |\n| onPopupClick | A callback of popup click | function(e) | - | 5.5.0 |\n\nConsult [Tooltip's documentation](/components/tooltip/#api) to find more APIs."
            },
            "Popover": {
              "description": "A simple popup menu to provide extra information or operations.\n\nComparing with `Tooltip`, besides information `Popover` card can also provide action elements like links and buttons.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Param   | Description         | Type                         | Default value | Version |\n| ------- | ------------------- | ---------------------------- | ------------- | ------- |\n| content | Content of the card | ReactNode \\| () => ReactNode | -             |         |\n| title   | Title of the card   | ReactNode \\| () => ReactNode | -             |         |\n\nConsult [Tooltip's documentation](/components/tooltip/#api) to find more APIs."
            },
            "Progress": {
              "description": "If it will take a long time to complete an operation, you can use `Progress` to show the current progress and status.\n\n- When an operation will interrupt the current interface, or it needs to run in the background for more than 2 seconds.\n- When you need to display the completion percentage of an operation.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\nProperties that shared by all types.\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| format | The template function of the content | function(percent, successPercent) | (percent) => percent + `%` | - |\n| percent | To set the completion percentage | number | 0 | - |\n| showInfo | Whether to display the progress value and the status icon | boolean | true |\n| status | To set the status of the Progress, options: `success` `exception` `normal` `active`(line only) | string | - |\n| strokeColor | The color of progress bar | string | - | - |\n| strokeLinecap | To set the style of the progress linecap | `round` \\| `butt` \\| `square`, see [stroke-linecap](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) | `round` | - |\n| success | Configs of successfully progress bar | { percent: number, strokeColor: string } | - | - |\n| trailColor | The color of unfilled part | string | - | - |\n| type | To set the type, options: `line` `circle` `dashboard` | string | `line` |\n| size | Progress size | number \\| \\[number \\| string, number] \\| \"small\" \\| \"default\" | \"default\" | v5.3.0 |\n\n### `type=\"line\"`\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| steps | The total step count | number | - | - |\n| strokeColor | The color of progress bar, render `linear-gradient` when passing an object, could accept `string[]` when has `steps`. | string \\| string[] \\| { from: string; to: string; direction: string } | - | 4.21.0: `string[]` |\n\n### `type=\"circle\"`\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| strokeColor | The color of circular progress, render gradient when passing an object | string \\| { number%: string } | - | - |\n| strokeWidth | To set the width of the circular progress, unit: percentage of the canvas width | number | 6 | - |\n\n### `type=\"dashboard\"`\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| gapDegree | The gap degree of half circle, 0 ~ 295 | number | 75 |\n| gapPosition | The gap position, options: `top` `bottom` `left` `right` | string | `bottom` |\n| strokeWidth | To set the width of the dashboard progress, unit: percentage of the canvas width | number | 6 |"
            },
            "QRCode": {
              "description": "Used when the text needs to be converted into a QR Code.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n> This component is available since `@private-basic-components@5.1.0`\n\n| Property | Description | Type | Default | Version |\n| :-- | :-- | :-- | :-- | :-- |\n| value | scanned text | string | - |\n| type | render type | `canvas \\| svg ` | `canvas` | 5.6.0 |\n| icon | include image url (only image link are supported) | string | - |\n| size | QRCode size | number | 128 |\n| iconSize | include image size | number | 32 |\n| color | QRCode Color | string | `#000` |\n| bgColor | QRCode Background Color | string | `transparent` | 5.5.0 |\n| bordered | Whether has border style | boolean | `true` |\n| errorLevel | Error Code Level | `'L' \\| 'M' \\| 'Q' \\| 'H' ` | `M` |\n| status | QRCode status | `active \\| expired \\| loading ` | `active` |\n| onRefresh | callback | `() => void` | - |"
            },
            "Radio": {
              "description": "- Used to select a single state from multiple options.\n- The difference from Select is that Radio is visible to the user and can facilitate the comparison of choice, which means there shouldn't be too many of them.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Radio/Radio.Button\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| autoFocus | Whether get focus when component mounted | boolean | false |\n| checked | Specifies whether the radio is selected | boolean | false |\n| defaultChecked | Specifies the initial state: whether or not the radio is selected | boolean | false |\n| disabled | Disable radio | boolean | false |\n| value | According to value for comparison, to determine whether the selected | any | - |\n\n### RadioGroup\n\nRadio group can wrap a group of `Radio`。\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| buttonStyle | The style type of radio button | `outline` \\| `solid` | `outline` |  |\n| defaultValue | Default selected value | any | - |  |\n| disabled | Disable all radio buttons | boolean | false |  |\n| name | The `name` property of all `input[type=\"radio\"]` children | string | - |  |\n| options | Set children optional | string\\[] \\| number\\[] \\| Array&lt;{ label: ReactNode; value: string; disabled?: boolean; }> | - |  |\n| optionType | Set Radio optionType | `default` \\| `button` | `default` | 4.4.0 |\n| size | The size of radio button style | `large` \\| `middle` \\| `small` | - |  |\n| value | Used for setting the currently selected value | any | - |  |\n| onChange | The callback function that is triggered when the state changes | function(e:Event) | - |  |"
            },
            "Rate": {
              "description": "- Show evaluation.\n- A quick rating operation on something.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | type | Default | Version |\n| --- | --- | --- | --- | --- |\n| allowClear | Whether to allow clear when click again | boolean | true |  |\n| allowHalf | Whether to allow semi selection | boolean | false |  |\n| autoFocus | If get focus when component mounted | boolean | false |  |\n| character | The custom character of rate | ReactNode \\| (RateProps) => ReactNode | &lt;StarFilled /> | function(): 4.4.0 |\n| className | The custom class name of rate | string | - |  |\n| count | Star count | number | 5 |  |\n| defaultValue | The default value | number | 0 |  |\n| disabled | If read only, unable to interact | boolean | false |  |\n| style | The custom style object of rate | CSSProperties | - |  |\n| tooltips | Customize tooltip by each character | string\\[] | - |  |\n| value | The current value | number | - |  |\n| onBlur | Callback when component lose focus | function() | - |  |\n| onChange | Callback when select value | function(value: number) | - |  |\n| onFocus | Callback when component get focus | function() | - |  |\n| onHoverChange | Callback when hover item | function(value: number) | - |  |\n| onKeyDown | Callback when keydown on component | function(event) | - |  |"
            },
            "Result": {
              "description": "Use when important operations need to inform the user to process the results and the feedback is more complicated.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| extra | Operating area | ReactNode | - |\n| icon | Custom back icon | ReactNode | - |\n| status | Result status, decide icons and colors | `success` \\| `error` \\| `info` \\| `warning` \\| `404` \\| `403` \\| `500` | `info` |\n| subTitle | The subTitle | ReactNode | - |\n| title | The title | ReactNode | - |"
            },
            "Segmented": {
              "description": "- When displaying multiple options and user can select a single option;\n- When switching the selected option, the content of the associated area changes.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n> This component is available since `@private-basic-components@4.20.0`\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| block | Option to fit width to its parent\\'s width | boolean | false |  |\n| defaultValue | Default selected value | string \\| number |  |  |\n| disabled | Disable all segments | boolean | false |  |\n| onChange | The callback function that is triggered when the state changes | function(value: string \\| number) |  |  |\n| options | Set children optional | string\\[] \\| number\\[] \\| Array<{ label: ReactNode value: string icon? ReactNode disabled?: boolean className?: string }> | [] |  |\n| size | The size of the Segmented. | `large` \\| `middle` \\| `small` | `middle` |  |\n| value | Currently selected value | string \\| number |  |  |"
            },
            "Select": {
              "description": "- A dropdown menu for displaying choices - an elegant alternative to the native `<select>` element.\n- Utilizing [Radio](/components/radio/) is recommended when there are fewer total options (less than 5).\n- You probably need [AutoComplete](/components/auto-complete/) if you're looking for an input box that can be typed or selected.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Select props\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| allowClear | Customize clear icon | boolean \\| { clearIcon?: ReactNode } | false | 5.8.0: Support object type |\n| autoClearSearchValue | Whether the current search will be cleared on selecting an item. Only applies when `mode` is set to `multiple` or `tags` | boolean | true |  |\n| autoFocus | Get focus by default | boolean | false |  |\n| bordered | Whether has border style | boolean | true |  |\n| defaultActiveFirstOption | Whether active first option by default | boolean | true |  |\n| defaultOpen | Initial open state of dropdown | boolean | - |  |\n| defaultValue | Initial selected option | string \\| string\\[] \\| <br />number \\| number\\[] \\| <br />LabeledValue \\| LabeledValue\\[] | - |  |\n| disabled | Whether disabled select | boolean | false |  |\n| popupClassName | The className of dropdown menu | string | - | 4.23.0 |\n| popupMatchSelectWidth | Determine whether the popup menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \\| number | true | 5.5.0 |\n| dropdownRender | Customize dropdown content | (originNode: ReactNode) => ReactNode | - |  |\n| dropdownStyle | The style of dropdown menu | CSSProperties | - |  |\n| fieldNames | Customize node label, value, options,groupLabel field name | object | { label: `label`, value: `value`, options: `options`, groupLabel: `label` } | 4.17.0 (`groupLabel` added in 5.6.0) |\n| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded | boolean \\| function(inputValue, option) | true |  |\n| filterSort | Sort function for search options sorting, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction | (optionA: Option, optionB: Option) => number | - | 4.9.0 |\n| getPopupContainer | Parent Node which the selector should be rendered to. Default to `body`. When position issues happen, try to modify it into scrollable content and position it relative. [Example](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body |  |\n| labelInValue | Whether to embed label in value, turn the format of value from `string` to { value: string, label: ReactNode } | boolean | false |  |\n| listHeight | Config popup height | number | 256 |  |\n| loading | Indicate loading state | boolean | false |  |\n| maxTagCount | Max tag count to show. `responsive` will cost render performance | number \\| `responsive` | - | responsive: 4.10 |\n| maxTagPlaceholder | Placeholder for not showing tags | ReactNode \\| function(omittedValues) | - |  |\n| maxTagTextLength | Max tag text length to show | number | - |  |\n| menuItemSelectedIcon | The custom menuItemSelected icon with multiple options | ReactNode | - |  |\n| mode | Set mode of Select | `multiple` \\| `tags` | - |  |\n| notFoundContent | Specify content to show when no result matches | ReactNode | `Not Found` |  |\n| open | Controlled open state of dropdown | boolean | - |  |\n| optionFilterProp | Which prop value of option will be used for filter if filterOption is true. If `options` is set, it should be set to `label` | string | `value` |  |\n| optionLabelProp | Which prop value of option will render as content of select. [Example](https://codesandbox.io/s/@private-basic-components-reproduction-template-tk678) | string | `children` |  |\n| options | Select options. Will get better perf than jsx definition | { label, value }\\[] | - |  |\n| placeholder | Placeholder of select | ReactNode | - |  |\n| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft |  |\n| removeIcon | The custom remove icon | ReactNode | - |  |\n| searchValue | The current input \"search\" text | string | - |  |\n| showSearch | Whether select is searchable | boolean | single: false, multiple: true |  |\n| size | Size of Select input | `large` \\| `middle` \\| `small` | `middle` |  |\n| status | Set validation status | 'error' \\| 'warning' | - | 4.19.0 |\n| suffixIcon | The custom suffix icon. Customize icon will not response click open to avoid icon designed to do other interactive. You can use `pointer-events: none` style to bypass | ReactNode | `<DownOutlined />` |  |\n| tagRender | Customize tag render, only applies when `mode` is set to `multiple` or `tags` | (props) => ReactNode | - |  |\n| tokenSeparators | Separator used to tokenize, only applies when `mode=\"tags\"` | string\\[] | - |  |\n| value | Current selected option (considered as a immutable array) | string \\| string\\[] \\| <br />number \\| number\\[] \\| <br />LabeledValue \\| LabeledValue\\[] | - |  |\n| virtual | Disable virtual scroll when set to false | boolean | true | 4.1.0 |\n| onBlur | Called when blur | function | - |  |\n| onChange | Called when select an option or input value change | function(value, option:Option \\| Array&lt;Option>) | - |  |\n| onClear | Called when clear | function | - | 4.6.0 |\n| onDeselect | Called when an option is deselected, param is the selected option's value. Only called for `multiple` or `tags`, effective in multiple or tags mode only | function(value: string \\| number \\| LabeledValue) | - |  |\n| onDropdownVisibleChange | Called when dropdown open | function(open) | - |  |\n| onFocus | Called when focus | function | - |  |\n| onInputKeyDown | Called when key pressed | function | - |  |\n| onMouseEnter | Called when mouse enter | function | - |  |\n| onMouseLeave | Called when mouse leave | function | - |  |\n| onPopupScroll | Called when dropdown scrolls | function | - |  |\n| onSearch | Callback function that is fired when input changed | function(value: string) | - |  |\n| onSelect | Called when an option is selected, the params are option's value (or key) and option instance | function(value: string \\| number \\| LabeledValue, option: Option) | - |  |\n\n> Note, if you find that the drop-down menu scrolls with the page, or you need to trigger Select in other popup layers, please try to use `getPopupContainer={triggerNode => triggerNode.parentElement}` to fix the drop-down popup rendering node in the parent element of the trigger .\n\n### Select Methods\n\n| Name    | Description  | Version |\n| ------- | ------------ | ------- |\n| blur()  | Remove focus |         |\n| focus() | Get focus    |         |\n\n### Option props\n\n| Property  | Description                          | Type             | Default | Version |\n| --------- | ------------------------------------ | ---------------- | ------- | ------- |\n| className | The additional class to option       | string           | -       |         |\n| disabled  | Disable this option                  | boolean          | false   |         |\n| title     | `title` attribute of Select Option   | string           | -       |         |\n| value     | Default to filter with this property | string \\| number | -       |         |\n\n### OptGroup props\n\n| Property | Description | Type                    | Default | Version |\n| -------- | ----------- | ----------------------- | ------- | ------- |\n| key      | Group key   | string                  | -       |         |\n| label    | Group label | string \\| React.Element | -       |         |"
            },
            "Skeleton": {
              "description": "- When a resource needs long time to load.\n- When the component contains lots of information, such as List or Card.\n- Only works when loading data for the first time.\n- Could be replaced by Spin in any situation, but can provide a better user experience.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Skeleton\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| active | Show animation effect | boolean | false |\n| avatar | Show avatar placeholder | boolean \\| [SkeletonAvatarProps](#skeletonavatarprops) | false |\n| loading | Display the skeleton when true | boolean | - |\n| paragraph | Show paragraph placeholder | boolean \\| [SkeletonParagraphProps](#skeletonparagraphprops) | true |\n| round | Show paragraph and title radius when true | boolean | false |\n| title | Show title placeholder | boolean \\| [SkeletonTitleProps](#skeletontitleprops) | true |\n\n### SkeletonAvatarProps\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| active | Show animation effect, only valid when used avatar independently | boolean | false |\n| shape | Set the shape of avatar | `circle` \\| `square` | - |\n| size | Set the size of avatar | number \\| `large` \\| `small` \\| `default` | - |\n\n### SkeletonTitleProps\n\n| Property | Description            | Type             | Default |\n| -------- | ---------------------- | ---------------- | ------- |\n| width    | Set the width of title | number \\| string | -       |\n\n### SkeletonParagraphProps\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| rows | Set the row count of paragraph | number | - |\n| width | Set the width of paragraph. When width is an Array, it can set the width of each row. Otherwise only set the last row width | number \\| string \\| Array&lt;number \\| string> | - |\n\n### SkeletonButtonProps\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| active | Show animation effect | boolean | false |  |\n| block | Option to fit button width to its parent width | boolean | false | 4.17.0 |\n| shape | Set the shape of button | `circle` \\| `round` \\| `square` \\| `default` | - |  |\n| size | Set the size of button | `large` \\| `small` \\| `default` | - |  |\n\n### SkeletonInputProps\n\n| Property | Description           | Type                            | Default |\n| -------- | --------------------- | ------------------------------- | ------- |\n| active   | Show animation effect | boolean                         | false   |\n| size     | Set the size of input | `large` \\| `small` \\| `default` | -       |"
            },
            "Slider": {
              "description": "To input a value in a range.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| autoAdjustOverflow | Whether to automatically adjust the popup position | boolean | true | 5.8.0 |\n| autoFocus | Whether get focus when component mounted | boolean | false |  |\n| defaultValue | The default value of slider. When `range` is false, use number, otherwise, use \\[number, number] | number \\| \\[number, number] | 0 \\| \\[0, 0] |  |\n| disabled | If true, the slider will not be intractable | boolean | false |  |\n| keyboard | Support using keyboard to move handlers | boolean | true | 5.2.0+ |\n| dots | Whether the thumb can drag over tick only | boolean | false |  |\n| included | Make effect when `marks` not null, true means containment and false means coordinative | boolean | true |  |\n| marks | Tick mark of Slider, type of key must be `number`, and must in closed interval \\[min, max], each mark can declare its own style | object | { number: ReactNode } \\| { number: { style: CSSProperties, label: ReactNode } } |  |\n| max | The maximum value the slider can slide to | number | 100 |  |\n| min | The minimum value the slider can slide to | number | 0 |  |\n| range | Dual thumb mode | boolean | false |  |\n| reverse | Reverse the component | boolean | false |  |\n| step | The granularity the slider can step through values. Must greater than 0, and be divided by (max - min) . When `marks` no null, `step` can be null | number \\| null | 1 |  |\n| tooltip | The tooltip relate props | [tooltip](#tooltip) | - | 4.23.0 |\n| value | The value of slider. When `range` is false, use number, otherwise, use \\[number, number] | number \\| \\[number, number] | - |  |\n| vertical | If true, the slider will be vertical | boolean | false |  |\n| onAfterChange | Fire when onmouseup is fired | (value) => void | - |  |\n| onChange | Callback function that is fired when the user changes the slider's value | (value) => void | - |  |\n\n### `styles` 和 `classNames` 属性\n\n| Property | Description                                 | Version |\n| -------- | ------------------------------------------- | ------- |\n| track    | The track between handle to handle in range | 5.10.0  |\n| tracks   | Who track in range                          | 5.10.0  |\n| rail     | Background rail                             | 5.10.0  |\n| handle   | The handle pointer                          | 5.10.0  |\n\n### range\n\n| Property       | Description                     | Type    | Default | Version |\n| -------------- | ------------------------------- | ------- | ------- | ------- |\n| draggableTrack | Whether range track can be drag | boolean | false   | 4.10.0  |\n\n### tooltip\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| open | If true, Tooltip will show always, or it will not show anyway, even if dragging or hovering | boolean | - | 4.23.0 |\n| placement | Set Tooltip display position. Ref [Tooltip](/components/tooltip/) | string | - | 4.23.0 |\n| getPopupContainer | The DOM container of the Tooltip, the default behavior is to create a div element in body | (triggerNode) => HTMLElement | () => document.body | 4.23.0 |\n| formatter | Slider will pass its value to `formatter`, and display its value in Tooltip, and hide Tooltip when return value is null | value => ReactNode \\| null | IDENTITY | 4.23.0 |"
            },
            "Space": {
              "description": "- Avoid components clinging together and set a unified space.\n- Use Space.Compact when child form components are compactly connected and the border is collapsed (After version `@private-basic-components@4.24.0` Supported).",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| align | Align items | `start` \\| `end` \\|`center` \\|`baseline` | - | 4.2.0 |\n| direction | The space direction | `vertical` \\| `horizontal` | `horizontal` | 4.1.0 |\n| size | The space size | [Size](#size) \\| [Size\\[\\]](#size) | `small` | 4.1.0 \\| Array: 4.9.0 |\n| split | Set split | ReactNode | - | 4.7.0 |\n| wrap | Auto wrap line, when `horizontal` effective | boolean | false | 4.9.0 |\n\n### Size\n\n`'small' | 'middle' | 'large' | number`\n\n### Space.Compact\n\nUse Space.Compact when child form components are compactly connected and the border is collapsed. The supported components are:\n\n- Button\n- AutoComplete\n- Cascader\n- DatePicker\n- Input/Input.Search\n- Select\n- TimePicker\n- TreeSelect\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| block | Option to fit width to its parent\\'s width | boolean | false | 4.24.0 |\n| direction | Set direction of layout | `vertical` \\| `horizontal` | `horizontal` | 4.24.0 |\n| size | Set child component size | `large` \\| `middle` \\| `small` | `middle` | 4.24.0 |\n\n### `styles` and `classNames` attribute\n\n<!-- prettier-ignore -->\n| Property | Description | Version |\n| -------- | ------------------------- | ------- |\n| item     | set `Space` child element | 5.6.0   |"
            },
            "Spin": {
              "description": "When part of the page is waiting for asynchronous data or during a rendering process, an appropriate loading animation can effectively alleviate users' inquietude.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| delay | Specifies a delay in milliseconds for loading state (prevent flush) | number (milliseconds) | - |\n| indicator | React node of the spinning indicator | ReactNode | - |\n| size | The size of Spin, options: `small`, `default` and `large` | string | `default` |\n| spinning | Whether Spin is visible | boolean | true |\n| tip | Customize description content when Spin has children | ReactNode | - |\n| wrapperClassName | The className of wrapper when Spin has children | string | - |\n\n### Static Method\n\n- `Spin.setDefaultIndicator(indicator: ReactNode)`\n\n  You can define default spin element globally."
            },
            "Statistic": {
              "description": "- When want to highlight some data.\n- When want to display statistic data with description.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n#### Statistic\n\n| Property         | Description                   | Type                 | Default | Version |\n| ---------------- | ----------------------------- | -------------------- | ------- | ------- |\n| decimalSeparator | The decimal separator         | string               | `.`     |         |\n| formatter        | Customize value display logic | (value) => ReactNode | -       |         |\n| groupSeparator   | Group separator               | string               | `,`     |         |\n| loading          | Loading status of Statistic   | boolean              | false   | 4.8.0   |\n| precision        | The precision of input value  | number               | -       |         |\n| prefix           | The prefix node of value      | ReactNode            | -       |         |\n| suffix           | The suffix node of value      | ReactNode            | -       |         |\n| title            | Display title                 | ReactNode            | -       |         |\n| value            | Display value                 | string \\| number     | -       |         |\n| valueStyle       | Set value section style       | CSSProperties        | -       |         |\n\n#### Statistic.Countdown\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| format | Format as [dayjs](https://day.js.org/) | string | `HH:mm:ss` |  |\n| prefix | The prefix node of value | ReactNode | - |  |\n| suffix | The suffix node of value | ReactNode | - |  |\n| title | Display title | ReactNode | - |  |\n| value | Set target countdown time | number \\| dayjs | - |  |\n| valueStyle | Set value section style | CSSProperties | - |  |\n| onFinish | Trigger when time's up | () => void | - |  |\n| onChange | Trigger when time's changing | (value: number) => void | - | 4.16.0 |"
            },
            "Steps": {
              "description": "When a given task is complicated or has a certain sequence in the series of subtasks, we can decompose it into several steps to make things easier.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Steps\n\nThe whole of the step bar.\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| className | Additional class to Steps | string | - |  |\n| current | To set the current step, counting from 0. You can overwrite this state by using `status` of `Step` | number | 0 |  |\n| direction | To specify the direction of the step bar, `horizontal` or `vertical` | string | `horizontal` |  |\n| initial | Set the initial step, counting from 0 | number | 0 |  |\n| labelPlacement | Place title and description with `horizontal` or `vertical` direction | string | `horizontal` |  |\n| percent | Progress circle percentage of current step in `process` status (only works on basic Steps) | number | - | 4.5.0 |\n| progressDot | Steps with progress dot style, customize the progress dot by setting it to a function. labelPlacement will be `vertical` | boolean \\| (iconDot, {index, status, title, description}) => ReactNode | false |  |\n| responsive | Change to vertical direction when screen width smaller than `532px` | boolean | true |  |\n| size | To specify the size of the step bar, `default` and `small` are currently supported | string | `default` |  |\n| status | To specify the status of current step, can be set to one of the following values: `wait` `process` `finish` `error` | string | `process` |  |\n| type | Type of steps, can be set to one of the following values: `default` `navigation` `inline` | string | `default` | inline: 5.0 |\n| onChange | Trigger when Step is changed | (current) => void | - |  |\n| items | StepItem content | [StepItem](#stepitem) | [] | 4.24.0 |\n\n### `type=\"inline\"`\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| className | Additional class to Steps | string | - |  |\n| current | To set the current step, counting from 0. You can overwrite this state by using `status` of `Step` | number | 0 |  |\n| initial | Set the initial step, counting from 0 | number | 0 |  |\n| status | To specify the status of current step, can be set to one of the following values: `wait` `process` `finish` `error` | string | `process` |  |\n| onChange | Trigger when Step is changed | (current) => void | - |  |\n| items | StepItem content. not supported: `icon` `subtitle` | [StepItem](#stepitem) | [] | 4.24.0 |\n\n### StepItem\n\nA single step in the step bar.\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| description | Description of the step, optional property | ReactNode | - |  |\n| disabled | Disable click | boolean | false |  |\n| icon | Icon of the step, optional property | ReactNode | - |  |\n| status | To specify the status. It will be automatically set by `current` of `Steps` if not configured. Optional values are: `wait` `process` `finish` `error` | string | `wait` |  |\n| subTitle | Subtitle of the step | ReactNode | - |  |\n| title | Title of the step | ReactNode | - |  |"
            },
            "Switch": {
              "description": "- If you need to represent the switching between two states or on-off state.\n- The difference between `Switch` and `Checkbox` is that `Switch` will trigger a state change directly when you toggle it, while `Checkbox` is generally used for state marking, which should work in conjunction with submit operation.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| autoFocus | Whether get focus when component mounted | boolean | false |\n| checked | Determine whether the Switch is checked | boolean | false |\n| checkedChildren | The content to be shown when the state is checked | ReactNode | - |\n| className | The additional class to Switch | string | - |\n| defaultChecked | Whether to set the initial state | boolean | false |\n| disabled | Disable switch | boolean | false |\n| loading | Loading state of switch | boolean | false |\n| size | The size of the Switch, options: `default` `small` | string | `default` |\n| unCheckedChildren | The content to be shown when the state is unchecked | ReactNode | - |\n| onChange | Trigger when the checked state is changing | function(checked: boolean, event: Event) | - |\n| onClick | Trigger when clicked | function(checked: boolean, event: Event) | - |"
            },
            "Table": {
              "description": "- To display a collection of structured data.\n- To sort, search, paginate, filter data.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Table\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| bordered | Whether to show all table borders | boolean | false |  |\n| columns | Columns of table | [ColumnsType](#column)\\[] | - |  |\n| components | Override default table elements | [TableComponents](https://github.com/react-component/table/blob/75ee0064e54a4b3215694505870c9d6c817e9e4a/src/interface.ts#L129) | - |  |\n| dataSource | Data record array to be displayed | object\\[] | - |  |\n| expandable | Config expandable content | [expandable](#expandable) | - |  |\n| footer | Table footer renderer | function(currentPageData) | - |  |\n| getPopupContainer | The render container of dropdowns in table | (triggerNode) => HTMLElement | () => TableHtmlElement |  |\n| loading | Loading status of table | boolean \\| [Spin Props](/components/spin/#api) | false |  |\n| locale | The i18n text including filter, sort, empty text, etc | object | [Default Value](https://github.com/@private-basic-components/@private-basic-components/blob/6dae4a7e18ad1ba193aedd5ab6867e1d823e2aa4/components/locale/en_US.tsx#L19-L37) |  |\n| pagination | Config of pagination. You can ref table pagination [config](#pagination) or full [`pagination`](/components/pagination/) document, hide it by setting it to `false` | object \\| `false` | - |  |\n| rowClassName | Row's className | function(record, index): string | - |  |\n| rowKey | Row's unique key, could be a string or function that returns a string | string \\| function(record): string | `key` |  |\n| rowSelection | Row selection [config](#rowselection) | object | - |  |\n| scroll | Whether the table can be scrollable, [config](#scroll) | object | - |  |\n| showHeader | Whether to show table header | boolean | true |  |\n| showSorterTooltip | The header show next sorter direction tooltip. It will be set as the property of Tooltip if its type is object | boolean \\| [Tooltip props](/components/tooltip/#api) | true |  |\n| size | Size of table | `large` \\| `middle` \\| `small` | `large` |  |\n| sortDirections | Supported sort way, could be `ascend`, `descend` | Array | \\[`ascend`, `descend`] |  |\n| sticky | Set sticky header and scroll bar | boolean \\| `{offsetHeader?: number, offsetScroll?: number, getContainer?: () => HTMLElement}` | - | 4.6.0 (getContainer: 4.7.0) |\n| summary | Summary content | (currentData) => ReactNode | - |  |\n| tableLayout | The [table-layout](https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout) attribute of table element | - \\| `auto` \\| `fixed` | -<hr />`fixed` when header/columns are fixed, or using `column.ellipsis` |  |\n| title | Table title renderer | function(currentPageData) | - |  |\n| onChange | Callback executed when pagination, filters or sorter is changed | function(pagination, filters, sorter, extra: { currentDataSource: \\[], action: `paginate` \\| `sort` \\| `filter` }) | - |  |\n| onHeaderRow | Set props on per header row | function(columns, index) | - |  |\n| onRow | Set props on per row | function(record, index) | - |  |\n| virtual | Support virtual list | boolean | - | 5.9.0 |\n\n#### onRow usage\n\nSame as `onRow` `onHeaderRow` `onCell` `onHeaderCell`\n\n```jsx\n<Table\n  onRow={(record, rowIndex) => {\n    return {\n      onClick: (event) => {}, // click row\n      onDoubleClick: (event) => {}, // double click row\n      onContextMenu: (event) => {}, // right button click row\n      onMouseEnter: (event) => {}, // mouse enter row\n      onMouseLeave: (event) => {}, // mouse leave row\n    };\n  }}\n  onHeaderRow={(columns, index) => {\n    return {\n      onClick: () => {}, // click header row\n    };\n  }}\n/>\n```\n\n### Column\n\nOne of the Table `columns` prop for describing the table's columns, Column has the same API.\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| align | The specify which way that column is aligned | `left` \\| `right` \\| `center` | `left` |  |\n| className | The className of this column | string | - |  |\n| colSpan | Span of this column's title | number | - |  |\n| dataIndex | Display field of the data record, support nest path by string array | string \\| string\\[] | - |  |\n| defaultFilteredValue | Default filtered values | string\\[] | - |  |\n| filterResetToDefaultFilteredValue | click the reset button, whether to restore the default filter | boolean | false |  |\n| defaultSortOrder | Default order of sorted values | `ascend` \\| `descend` | - |  |\n| ellipsis | The ellipsis cell content, not working with sorter and filters for now.<br />tableLayout would be `fixed` when `ellipsis` is `true` or `{ showTitle?: boolean }` | boolean \\| {showTitle?: boolean } | false | showTitle: 4.3.0 |\n| filterDropdown | Customized filter overlay | ReactNode \\| (props: [FilterDropdownProps](https://github.com/@private-basic-components/@private-basic-components/blob/ecc54dda839619e921c0ace530408871f0281c2a/components/table/interface.tsx#L79)) => ReactNode | - |  |\n| filterDropdownOpen | Whether `filterDropdown` is visible | boolean | - |  |\n| filtered | Whether the `dataSource` is filtered | boolean | false |  |\n| filteredValue | Controlled filtered value, filter icon will highlight | string\\[] | - |  |\n| filterIcon | Customized filter icon | ReactNode \\| (filtered: boolean) => ReactNode | - |  |\n| filterMultiple | Whether multiple filters can be selected | boolean | true |  |\n| filterMode | To specify the filter interface | 'menu' \\| 'tree' | 'menu' | 4.17.0 |\n| filterSearch | Whether to be searchable for filter menu | boolean \\| function(input, record):boolean | false | boolean:4.17.0 function:4.19.0 |\n| filters | Filter menu config | object\\[] | - |  |\n| fixed | (IE not support) Set column to be fixed: `true`(same as left) `'left'` `'right'` | boolean \\| string | false |  |\n| key | Unique key of this column, you can ignore this prop if you've set a unique `dataIndex` | string | - |  |\n| render | Renderer of the table cell. The return value should be a ReactNode | function(text, record, index) {} | - |  |\n| responsive | The list of breakpoints at which to display this column. Always visible if not set | [Breakpoint](https://github.com/@private-basic-components/@private-basic-components/blob/015109b42b85c63146371b4e32b883cf97b088e8/components/_util/responsiveObserve.ts#L1)\\[] | - | 4.2.0 |\n| rowScope | Set scope attribute for all cells in this column | `row` \\| `rowgroup` | - | 5.1.0 |\n| shouldCellUpdate | Control cell render logic | (record, prevRecord) => boolean | - | 4.3.0 |\n| showSorterTooltip | If header show next sorter direction tooltip, override `showSorterTooltip` in table | boolean \\| [Tooltip props](/components/tooltip/) | true |  |\n| sortDirections | Supported sort way, override `sortDirections` in `Table`, could be `ascend`, `descend` | Array | \\[`ascend`, `descend`] |  |\n| sorter | Sort function for local sort, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction. If you need sort buttons only, set to `true` | function \\| boolean \\| { compare: function, multiple: number } | - |  |\n| sortOrder | Order of sorted values: `ascend` `descend` `null` | `ascend` \\| `descend` \\| null | - |  |\n| sortIcon | Customized sort icon | (props: { sortOrder }) => ReactNode | - | 5.6.0 |\n| title | Title of this column | ReactNode \\| ({ sortOrder, sortColumn, filters }) => ReactNode | - |  |\n| width | Width of this column ([width not working?](https://github.com/@private-basic-components/@private-basic-components/issues/13825#issuecomment-449889241)) | string \\| number | - |  |\n| onCell | Set props on per cell | function(record, rowIndex) | - |  |\n| onFilter | Function that determines if the row is displayed when filtered | function(value, record) => boolean | - |  |\n| onFilterDropdownOpenChange | Callback executed when `filterDropdownOpen` is changed | function(visible) {} | - |  |\n| onHeaderCell | Set props on per header cell | function(column) | - |  |\n\n### ColumnGroup\n\n| Property | Description               | Type      | Default |\n| -------- | ------------------------- | --------- | ------- |\n| title    | Title of the column group | ReactNode | -       |\n\n### pagination\n\nProperties for pagination.\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| position | Specify the position of `Pagination`, could be`topLeft` \\| `topCenter` \\| `topRight` \\|`bottomLeft` \\| `bottomCenter` \\| `bottomRight` | Array | \\[`bottomRight`] |\n\nMore about pagination, please check [`Pagination`](/components/pagination/).\n\n### expandable\n\nProperties for expandable.\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| childrenColumnName | The column contains children to display | string | children |  |\n| columnTitle | Set the title of the expand column | ReactNode | - | 4.23.0 |\n| columnWidth | Set the width of the expand column | string \\| number | - |  |\n| defaultExpandAllRows | Expand all rows initially | boolean | false |  |\n| defaultExpandedRowKeys | Initial expanded row keys | string\\[] | - |  |\n| expandedRowClassName | Expanded row's className | function(record, index, indent): string | - |  |\n| expandedRowKeys | Current expanded row keys | string\\[] | - |  |\n| expandedRowRender | Expanded container render for each row | function(record, index, indent, expanded): ReactNode | - |  |\n| expandIcon | Customize row expand Icon. Ref [example](https://codesandbox.io/s/fervent-bird-nuzpr) | function(props): ReactNode | - |  |\n| expandRowByClick | Whether to expand row by clicking anywhere in the whole row | boolean | false |  |\n| fixed | Whether the expansion icon is fixed. Optional true `left` `right` | boolean \\| string | false | 4.16.0 |\n| indentSize | Indent size in pixels of tree data | number | 15 |  |\n| rowExpandable | Enable row can be expandable | (record) => boolean | - |  |\n| showExpandColumn | Show expand column | boolean | true | 4.18.0 |\n| onExpand | Callback executed when the row expand icon is clicked | function(record, event) | - |  |\n| onExpandedRowsChange | Callback executed when the expanded rows change | function(expandedRows) | - |  |\n\n### rowSelection\n\nProperties for row selection.\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| checkStrictly | Check table row precisely; parent row and children rows are not associated | boolean | true | 4.4.0 |\n| columnTitle | Set the title of the selection column | ReactNode | - |  |\n| columnWidth | Set the width of the selection column | string \\| number | `32px` |  |\n| fixed | Fixed selection column on the left | boolean | - |  |\n| getCheckboxProps | Get Checkbox or Radio props | function(record) | - |  |\n| hideSelectAll | Hide the selectAll checkbox and custom selection | boolean | false | 4.3.0 |\n| preserveSelectedRowKeys | Keep selection `key` even when it removed from `dataSource` | boolean | - | 4.4.0 |\n| renderCell | Renderer of the table cell. Same as `render` in column | function(checked, record, index, originNode) {} | - | 4.1.0 |\n| selectedRowKeys | Controlled selected row keys | string\\[] \\| number\\[] | \\[] |  |\n| selections | Custom selection [config](#selection), only displays default selections when set to `true` | object\\[] \\| boolean | - |  |\n| type | `checkbox` or `radio` | `checkbox` \\| `radio` | `checkbox` |  |\n| onCell | Set props on per cell. Same as `onCell` in column | function(record, rowIndex) | - | 5.5.0 |\n| onChange | Callback executed when selected rows change | function(selectedRowKeys, selectedRows, info: { type }) | - | `info.type`: 4.21.0 |\n| onSelect | Callback executed when select/deselect one row | function(record, selected, selectedRows, nativeEvent) | - |  |\n| onSelectAll | Callback executed when select/deselect all rows | function(selected, selectedRows, changeRows) | - |  |\n| onSelectInvert | Callback executed when row selection is inverted | function(selectedRowKeys) | - |  |\n| onSelectNone | Callback executed when row selection is cleared | function() | - |  |\n| onSelectMultiple | Callback executed when row selection is changed by pressing shift | function(selected, selectedRows, changeRows) | - |  |\n\n### scroll\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| scrollToFirstRowOnChange | Whether to scroll to the top of the table when paging, sorting, filtering changes | boolean | - |\n| x | Set horizontal scrolling, can also be used to specify the width of the scroll area, could be number, percent value, true and ['max-content'](https://developer.mozilla.org/zh-CN/docs/Web/CSS/width#max-content) | string \\| number \\| true | - |\n| y | Set vertical scrolling, can also be used to specify the height of the scroll area, could be string or number | string \\| number | - |\n\n### selection\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| key | Unique key of this selection | string | - |\n| text | Display text of this selection | ReactNode | - |\n| onSelect | Callback executed when this selection is clicked | function(changeableRowKeys) | - |"
            },
            "Tabs": {
              "description": "@private-basic-components has 3 types of Tabs for different situations.\n\n- Card Tabs: for managing too many closeable views.\n- Normal Tabs: for functional aspects of a page.\n- [Radio.Button](/components/radio/#components-radio-demo-radiobutton): for secondary tabs.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Tabs\n\n<!-- prettier-ignore -->\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| activeKey | Current TabPane's key | string | - |  |\n| addIcon | Customize add icon | ReactNode | - | 4.4.0 |\n| animated | Whether to change tabs with animation. | boolean \\| { inkBar: boolean, tabPane: boolean } | { inkBar: true, tabPane: false } |  |\n| centered | Centers tabs | boolean | false | 4.4.0 |\n| defaultActiveKey | Initial active TabPane's key, if `activeKey` is not set | string | - |  |\n| hideAdd | Hide plus icon or not. Only works while `type=\"editable-card\"` | boolean | false |  |\n| indicatorSize | Customize length of indicator, which is the same as tab by default | number \\| (origin: number) => number | - | 5.9.0 |\n| items | Configure tab content | [TabItemType](#tabitemtype) | [] | 4.23.0 |\n| moreIcon | The custom icon of ellipsis | ReactNode | &lt;EllipsisOutlined /> | 4.14.0 |\n| popupClassName | `className` for more dropdown. | string | - | 4.21.0 |\n| renderTabBar | Replace the TabBar | (props: DefaultTabBarProps, DefaultTabBar: React.ComponentClass) => React.ReactElement | - |  |\n| size | Preset tab bar size | `large` \\| `middle` \\| `small` | `middle` |  |\n| tabBarExtraContent | Extra content in tab bar | ReactNode \\| {left?: ReactNode, right?: ReactNode} | - | object: 4.6.0 |\n| tabBarGutter | The gap between tabs | number | - |  |\n| tabBarStyle | Tab bar style object | CSSProperties | - |  |\n| tabPosition | Position of tabs | `top` \\| `right` \\| `bottom` \\| `left` | `top` |  |\n| destroyInactiveTabPane | Whether destroy inactive TabPane when change tab | boolean | false |  |\n| type | Basic style of tabs | `line` \\| `card` \\| `editable-card` | `line` |  |\n| onChange | Callback executed when active tab is changed | function(activeKey) {} | - |  |\n| onEdit | Callback executed when tab is added or removed. Only works while `type=\"editable-card\"` | (action === 'add' ? event : targetKey, action): void | - |  |\n| onTabClick | Callback executed when tab is clicked | function(key: string, event: MouseEvent) | - |  |\n| onTabScroll | Trigger when tab scroll | function({ direction: `left` \\| `right` \\| `top` \\| `bottom` }) | - | 4.3.0 |\n\nMore option at [rc-tabs tabs](https://github.com/react-component/tabs#tabs)\n\n### TabItemType\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| closeIcon | Customize close icon in TabPane's head. Only works while `type=\"editable-card\"`. 5.7.0: close button will be hidden when setting to `null` or `false` | boolean \\| ReactNode | - |\n| disabled | Set TabPane disabled | boolean | false |\n| forceRender | Forced render of content in tabs, not lazy render after clicking on tabs | boolean | false |\n| key | TabPane's key | string | - |\n| label | TabPane's head display text | ReactNode | - |\n| children | TabPane's head display content | ReactNode | - |"
            },
            "Tag": {
              "description": "- It can be used to tag by dimension or property.\n\n- When categorizing.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Tag\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| closeIcon | Custom close icon. 5.7.0: close button will be hidden when setting to `null` or `false` | ReactNode | false | 4.4.0 |\n| color | Color of the Tag | string | - |  |\n| icon | Set the icon of tag | ReactNode | - |  |\n| bordered | Whether has border style | boolean | true | 5.4.0 |\n| onClose | Callback executed when tag is closed | (e) => void | - |  |\n\n### Tag.CheckableTag\n\n| Property | Description                                     | Type              | Default |\n| -------- | ----------------------------------------------- | ----------------- | ------- |\n| checked  | Checked status of Tag                           | boolean           | false   |\n| onChange | Callback executed when Tag is checked/unchecked | (checked) => void | -       |"
            },
            "TimePicker": {
              "description": "By clicking the input box, you can select a time from a popup panel.",
              "api": "---\n\nCommon props ref:[Common props](/docs/react/common-props)\n\n```jsx\nimport dayjs from 'dayjs';\nimport customParseFormat from 'dayjs/plugin/customParseFormat'\n\ndayjs.extend(customParseFormat)\n\n<TimePicker defaultValue={dayjs('13:30:56', 'HH:mm:ss')} />;\n```\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| allowClear | Customize clear icon | boolean \\| { clearIcon?: ReactNode } | true | 5.8.0: Support object type |\n| autoFocus | If get focus when component mounted | boolean | false |  |\n| bordered | Whether has border style | boolean | true |  |\n| cellRender | Custom rendering function for picker cells | (current: number, info: { originNode: React.ReactElement, today: dayjs, range?: 'start' \\| 'end', subType: 'hour' \\| 'minute' \\| 'second' \\| 'meridiem' }) => React.ReactNode | - | 5.4.0 |\n| changeOnBlur | Trigger `change` when blur. e.g. datetime picker no need click confirm button | boolean | false | 5.5.0 |\n| className | The className of picker | string | - |  |\n| defaultValue | To set default time | [dayjs](http://day.js.org/) | - |  |\n| disabled | Determine whether the TimePicker is disabled | boolean | false |  |\n| disabledTime | To specify the time that cannot be selected | [DisabledTime](#disabledtime) | - | 4.19.0 |\n| format | To set the time format | string | `HH:mm:ss` |  |\n| getPopupContainer | To set the container of the floating layer, while the default is to create a div element in body | function(trigger) | - |  |\n| hideDisabledOptions | Whether hide the options that can not be selected | boolean | false |  |\n| hourStep | Interval between hours in picker | number | 1 |  |\n| inputReadOnly | Set the `readonly` attribute of the input tag (avoids virtual keyboard on touch devices) | boolean | false |  |\n| minuteStep | Interval between minutes in picker | number | 1 |  |\n| open | Whether to popup panel | boolean | false |  |\n| placeholder | Display when there's no value | string \\| \\[string, string] | `Select a time` |  |\n| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft |  |\n| popupClassName | The className of panel | string | - |  |\n| popupStyle | The style of panel | CSSProperties | - |  |\n| renderExtraFooter | Called from time picker panel to render some addon to its bottom | () => ReactNode | - |  |\n| secondStep | Interval between seconds in picker | number | 1 |  |\n| showNow | Whether to show `Now` button on panel | boolean | - | 4.4.0 |\n| size | To determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | `large` \\| `middle` \\| `small` | - |  |\n| status | Set validation status | 'error' \\| 'warning' \\| 'success' \\| 'validating' | - | 4.19.0 |\n| suffixIcon | The custom suffix icon | ReactNode | - |  |\n| use12Hours | Display as 12 hours format, with default format `h:mm:ss a` | boolean | false |  |\n| value | To set time | [dayjs](http://day.js.org/) | - |  |\n| onChange | A callback function, can be executed when the selected time is changing | function(time: dayjs, timeString: string): void | - |  |\n| onOpenChange | A callback function which will be called while panel opening/closing | (open: boolean) => void | - |  |\n| onSelect | A callback function, executes when a value is selected | function(time: dayjs): void | - |  |\n\n#### DisabledTime\n\n```typescript\ntype DisabledTime = (now: Dayjs) => {\n  disabledHours?: () => number[];\n  disabledMinutes?: (selectedHour: number) => number[];\n  disabledSeconds?: (selectedHour: number, selectedMinute: number) => number[];\n};\n```"
            },
            "Timeline": {
              "description": "- When a series of information needs to be ordered by time (ascending or descending).\n- When you need a timeline to make a visual connection.\n\n<Alert message=\"After version 5.2.0, we provide a simpler usage &lt;Timeline items={[...]} /&gt; with better performance and potential of writing simpler code style in your applications. Meanwhile, we deprecated the old usage in browser console, we will remove it in @private-basic-components 6.0.\"></Alert>\n\n```jsx\n// works when >=5.2.0, recommended ✅\nconst items = [{ children: 'sample', label: 'sample' }];\nreturn <Timeline items={items} />;\n\n// works when <5.2.0, deprecated when >=5.2.0 🙅🏻‍♀️\nreturn (\n  <Timeline onChange={onChange}>\n    <Timeline.Item>Sample</Timeline.Item>\n  </Timeline>\n);\n```",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Timeline\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| mode | By sending `alternate` the timeline will distribute the nodes to the left and right | `left` \\| `alternate` \\| `right` | - |\n| pending | Set the last ghost node's existence or its content | boolean \\| ReactNode | false |\n| pendingDot | Set the dot of the last ghost node when pending is true | ReactNode | &lt;LoadingOutlined /&gt; |\n| reverse | Whether reverse nodes or not | boolean | false |\n| items | Each node of timeline | [Items](#Items)[] | 5.2.0 |\n\n### Items\n\nNode of timeline.\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| color | Set the circle's color to `blue`, `red`, `green`, `gray` or other custom colors | string | `blue` |\n| dot | Customize timeline dot | ReactNode | - |\n| label | Set the label | ReactNode | - |\n| children | Set the content | ReactNode | - |\n| position | Customize node position | `left` \\| `right` | - |"
            },
            "Tooltip": {
              "description": "- The tip is shown on mouse enter, and is hidden on mouse leave. The Tooltip doesn't support complex text or operations.\n- To provide an explanation of a `button/text/operation`. It's often used instead of the html `title` attribute.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description                   | Type                         | Default |\n| -------- | ----------------------------- | ---------------------------- | ------- |\n| title    | The text shown in the tooltip | ReactNode \\| () => ReactNode | -       |\n\n### Common API\n\nThe following APIs are shared by Tooltip, Popconfirm, Popover.\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| align | This value will be merged into placement's config, please refer to the settings [rc-tooltip](https://github.com/react-component/tooltip) | object | - |  |\n| arrow | Change arrow's visible state and change whether the arrow is pointed at the center of target. | boolean \\| { pointAtCenter: boolean } | true | 5.2.0 |\n| autoAdjustOverflow | Whether to adjust popup placement automatically when popup is off screen | boolean | true |  |\n| color | The background color | string | - | 4.3.0 |\n| defaultOpen | Whether the floating tooltip card is open by default | boolean | false | 4.23.0 |\n| destroyTooltipOnHide | Whether destroy tooltip when hidden | boolean | false |  |\n| fresh | Tooltip will cache content when it is closed by default. Setting this property will always keep updating | boolean | false | 5.10.0 |\n| getPopupContainer | The DOM container of the tip, the default behavior is to create a `div` element in `body` | (triggerNode: HTMLElement) => HTMLElement | () => document.body |  |\n| mouseEnterDelay | Delay in seconds, before tooltip is shown on mouse enter | number | 0.1 |  |\n| mouseLeaveDelay | Delay in seconds, before tooltip is hidden on mouse leave | number | 0.1 |  |\n| overlayClassName | Class name of the tooltip card | string | - |  |\n| overlayStyle | Style of the tooltip card | object | - |  |\n| overlayInnerStyle | Style of the tooltip inner content | object | - |  |\n| placement | The position of the tooltip relative to the target, which can be one of `top` `left` `right` `bottom` `topLeft` `topRight` `bottomLeft` `bottomRight` `leftTop` `leftBottom` `rightTop` `rightBottom` | string | `top` |  |\n| trigger | Tooltip trigger mode. Could be multiple by passing an array | `hover` \\| `focus` \\| `click` \\| `contextMenu` \\| Array&lt;string> | `hover` |  |\n| open | Whether the floating tooltip card is open or not. Use `visible` under 4.23.0 ([why?](/docs/react/faq#why-open)) | boolean | false | 4.23.0 |\n| zIndex | Config `z-index` of Tooltip | number | - |  |\n| onOpenChange | Callback executed when visibility of the tooltip card is changed | (open: boolean) => void | - | 4.23.0 |"
            },
            "Tour": {
              "description": "Use when you want to guide users through a product.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Tour\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| arrow | Whether to show the arrow, including the configuration whether to point to the center of the element | `boolean`\\|`{ pointAtCenter: boolean}` | `true` |  |\n| closeIcon | Customize close icon | `React.ReactNode` | `true` | 5.9.0 |\n| placement | Position of the guide card relative to the target element | `center` `left` `leftTop` `leftBottom` `right` `rightTop` `rightBottom` `top` `topLeft` `topRight` `bottom` `bottomLeft` `bottomRight` | `bottom` |  |\n| onClose | Callback function on shutdown | `Function` | - |  |\n| mask | Whether to enable masking, change mask style and fill color by pass custom props | `boolean \\| { style?: React.CSSProperties; color?: string; }` | `true` |  |\n| type | Type, affects the background color and text color | `default` `primary` | `default` |  |\n| open | Open tour | `boolean` | - |  |\n| onChange | Callback when the step changes. Current is the previous step | `(current: number) => void` | - |  |\n| current | What is the current step | `number` | - |  |\n| scrollIntoViewOptions | support pass custom scrollIntoView options | `boolean \\| ScrollIntoViewOptions` | `true` | 5.2.0 |\n| indicatorsRender | custom indicator | `(current: number, total: number) => ReactNode` | - | 5.2.0 |\n| zIndex | Tour's zIndex | number | 1001 | 5.3.0 |\n\n### TourStep\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| target | Get the element the guide card points to. Empty makes it show in center of screen | `() => HTMLElement` `HTMLElement` | - |  |\n| arrow | Whether to show the arrow, including the configuration whether to point to the center of the element | `boolean` `{ pointAtCenter: boolean}` | `true` |  |\n| closeIcon | Customize close icon | `React.ReactNode` | `true` | 5.9.0 |\n| cover | Displayed pictures or videos | `ReactNode` | - |  |\n| title | title | `ReactNode` | - |  |\n| description | description | `ReactNode` | - |  |\n| placement | Position of the guide card relative to the target element | `center` `left` `leftTop` `leftBottom` `right` `rightTop` `rightBottom` `top` `topLeft` `topRight` `bottom` `bottomLeft` `bottomRight` | `bottom` |  |\n| onClose | Callback function on shutdown | `Function` | - |  |\n| mask | Whether to enable masking, change mask style and fill color by pass custom props, the default follows the `mask` property of Tour | `boolean \\| { style?: React.CSSProperties; color?: string; }` | `true` |  |\n| type | Type, affects the background color and text color | `default` `primary` | `default` |  |\n| nextButtonProps | Properties of the Next button | `{ children: ReactNode; onClick: Function }` | - |  |\n| prevButtonProps | Properties of the previous button | `{ children: ReactNode; onClick: Function }` | - |  |\n| scrollIntoViewOptions | support pass custom scrollIntoView options, the default follows the `scrollIntoViewOptions` property of Tour | `boolean \\| ScrollIntoViewOptions` | `true` | 5.2.0 |"
            },
            "Transfer": {
              "description": "- It is a select control essentially which can be use for selecting multiple items.\n- Transfer can display more information for items and take up more space.\n\nTransfer the elements between two columns in an intuitive and efficient way.\n\nOne or more elements can be selected from either column, one click on the proper `direction` button, and the transfer is done. The left column is considered the `source` and the right column is considered the `target`. As you can see in the API description, these names are reflected in.\n\n> notice: Transfer is a controlled component, uncontrolled mode is not supported.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| dataSource | Used for setting the source data. The elements that are part of this array will be present the left column. Except the elements whose keys are included in `targetKeys` prop | [RecordType extends TransferItem = TransferItem](https://github.com/@private-basic-components/@private-basic-components/blob/1bf0bab2a7bc0a774119f501806e3e0e3a6ba283/components/transfer/index.tsx#L12)\\[] | \\[] |  |\n| disabled | Whether disabled transfer | boolean | false |  |\n| selectionsIcon | custom dropdown icon | React.ReactNode |  | 5.8.0 |\n| filterOption | A function to determine whether an item should show in search result list, only works when searching, (add `direction` support since 5.9.0+) | (inputValue, option, direction: `left` \\| `right`): boolean | - |  |\n| footer | A function used for rendering the footer | (props, { direction }) => ReactNode | - | direction: 4.17.0 |\n| listStyle | A custom CSS style used for rendering the transfer columns | object \\| ({direction: `left` \\| `right`}) => object | - |  |\n| locale | The i18n text including filter, empty text, item unit, etc | { itemUnit: string; itemsUnit: string; searchPlaceholder: string; notFoundContent: ReactNode \\| ReactNode[]; } | { itemUnit: `item`, itemsUnit: `items`, notFoundContent: `The list is empty`, searchPlaceholder: `Search here` } |  |\n| oneWay | Display as single direction style | boolean | false | 4.3.0 |\n| operations | A set of operations that are sorted from top to bottom | string\\[] | \\[`>`, `<`] |  |\n| operationStyle | A custom CSS style used for rendering the operations column | object | - |  |\n| pagination | Use pagination. Not work in render props | boolean \\| { pageSize: number, simple: boolean, showSizeChanger?: boolean, showLessItems?: boolean } | false | 4.3.0 |\n| render | The function to generate the item shown on a column. Based on an record (element of the dataSource array), this function should return a React element which is generated from that record. Also, it can return a plain object with `value` and `label`, `label` is a React element and `value` is for title | (record) => ReactNode | - |  |\n| selectAllLabels | A set of customized labels for select all checkboxes on the header | (ReactNode \\| (info: { selectedCount: number, totalCount: number }) => ReactNode)\\[] | - |  |\n| selectedKeys | A set of keys of selected items | string\\[] | \\[] |  |\n| showSearch | If included, a search box is shown on each column | boolean | false |  |\n| showSelectAll | Show select all checkbox on the header | boolean | true |  |\n| status | Set validation status | 'error' \\| 'warning' | - | 4.19.0 |\n| targetKeys | A set of keys of elements that are listed on the right column | string\\[] | \\[] |  |\n| titles | A set of titles that are sorted from left to right | ReactNode\\[] | - |  |\n| onChange | A callback function that is executed when the transfer between columns is complete | (targetKeys, direction, moveKeys): void | - |  |\n| onScroll | A callback function which is executed when scroll options list | (direction, event): void | - |  |\n| onSearch | A callback function which is executed when search field are changed | (direction: `left` \\| `right`, value: string): void | - |  |\n| onSelectChange | A callback function which is executed when selected items are changed | (sourceSelectedKeys, targetSelectedKeys): void | - |  |\n\n### Render Props\n\nTransfer accept `children` to customize render list, using follow props:\n\n| Property        | Description             | Type                                 | Version |\n| --------------- | ----------------------- | ------------------------------------ | ------- |\n| direction       | List render direction   | `left` \\| `right`                    |         |\n| disabled        | Disable list or not     | boolean                              |         |\n| filteredItems   | Filtered items          | RecordType\\[]                        |         |\n| selectedKeys    | Selected items          | string\\[]                            |         |\n| onItemSelect    | Select item             | (key: string, selected: boolean)     |         |\n| onItemSelectAll | Select a group of items | (keys: string\\[], selected: boolean) |         |\n\n#### example\n\n```jsx\n<Transfer {...props}>{(listProps) => <YourComponent {...listProps} />}</Transfer>\n```"
            },
            "Tree": {
              "description": "Almost anything can be represented in a tree structure. Examples include directories, organization hierarchies, biological classifications, countries, etc. The `Tree` component is a way of representing the hierarchical relationship between these things. You can also expand, collapse, and select a treeNode within a `Tree`.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Tree props\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| allowDrop | Whether to allow dropping on the node | ({ dropNode, dropPosition }) => boolean | - |  |\n| autoExpandParent | Whether to automatically expand a parent treeNode | boolean | false |  |\n| blockNode | Whether treeNode fill remaining horizontal space | boolean | false |  |\n| checkable | Add a Checkbox before the treeNodes | boolean | false |  |\n| checkedKeys | (Controlled) Specifies the keys of the checked treeNodes (PS: When this specifies the key of a treeNode which is also a parent treeNode, all the children treeNodes of will be checked; and vice versa, when it specifies the key of a treeNode which is a child treeNode, its parent treeNode will also be checked. When `checkable` and `checkStrictly` is true, its object has `checked` and `halfChecked` property. Regardless of whether the child or parent treeNode is checked, they won't impact each other | string\\[] \\| {checked: string\\[], halfChecked: string\\[]} | \\[] |  |\n| checkStrictly | Check treeNode precisely; parent treeNode and children treeNodes are not associated | boolean | false |  |\n| defaultCheckedKeys | Specifies the keys of the default checked treeNodes | string\\[] | \\[] |  |\n| defaultExpandAll | Whether to expand all treeNodes by default | boolean | false |  |\n| defaultExpandedKeys | Specify the keys of the default expanded treeNodes | string\\[] | \\[] |  |\n| defaultExpandParent | If auto expand parent treeNodes when init | boolean | true |  |\n| defaultSelectedKeys | Specifies the keys of the default selected treeNodes | string\\[] | \\[] |  |\n| disabled | Whether disabled the tree | boolean | false |  |\n| draggable | Specifies whether this Tree or the node is draggable. Use `icon: false` to disable drag handler icon | boolean \\| ((node: DataNode) => boolean) \\| { icon?: React.ReactNode \\| false, nodeDraggable?: (node: DataNode) => boolean } | false | `config`: 4.17.0 |\n| expandedKeys | (Controlled) Specifies the keys of the expanded treeNodes | string\\[] | \\[] |  |\n| fieldNames | Customize node title, key, children field name | object | { title: `title`, key: `key`, children: `children` } | 4.17.0 |\n| filterTreeNode | Defines a function to filter (highlight) treeNodes. When the function returns `true`, the corresponding treeNode will be highlighted | function(node) | - |  |\n| height | Config virtual scroll height. Will not support horizontal scroll when enable this | number | - |  |\n| icon | Customize treeNode icon | ReactNode \\| (props) => ReactNode | - |  |\n| loadData | Load data asynchronously | function(node) | - |  |\n| loadedKeys | (Controlled) Set loaded tree nodes. Need work with `loadData` | string\\[] | \\[] |  |\n| multiple | Allows selecting multiple treeNodes | boolean | false |  |\n| rootStyle | Style on the root element | CSSProperties | - | 4.20.0 |\n| selectable | Whether can be selected | boolean | true |  |\n| selectedKeys | (Controlled) Specifies the keys of the selected treeNodes, multiple selection needs to set `multiple` to true | string\\[] | - |  |\n| showIcon | Shows the icon before a TreeNode's title. There is no default style; you must set a custom style for it if set to true | boolean | false |  |\n| showLine | Shows a connecting line | boolean \\| {showLeafIcon: boolean \\| ReactNode \\| ((props: AntTreeNodeProps) => ReactNode)} | false |  |\n| switcherIcon | Customize collapse/expand icon of tree node | ReactNode \\| ((props: AntTreeNodeProps) => ReactNode) | - | renderProps: 4.20.0 |\n| titleRender | Customize tree node title render | (nodeData) => ReactNode | - | 4.5.0 |\n| treeData | The treeNodes data Array, if set it then you need not to construct children TreeNode. (key should be unique across the whole array) | array&lt;{ key, title, children, \\[disabled, selectable] }> | - |  |\n| virtual | Disable virtual scroll when set to false | boolean | true | 4.1.0 |\n| onCheck | Callback function for when the onCheck event occurs | function(checkedKeys, e:{checked: bool, checkedNodes, node, event, halfCheckedKeys}) | - |  |\n| onDragEnd | Callback function for when the onDragEnd event occurs | function({event, node}) | - |  |\n| onDragEnter | Callback function for when the onDragEnter event occurs | function({event, node, expandedKeys}) | - |  |\n| onDragLeave | Callback function for when the onDragLeave event occurs | function({event, node}) | - |  |\n| onDragOver | Callback function for when the onDragOver event occurs | function({event, node}) | - |  |\n| onDragStart | Callback function for when the onDragStart event occurs | function({event, node}) | - |  |\n| onDrop | Callback function for when the onDrop event occurs | function({event, node, dragNode, dragNodesKeys}) | - |  |\n| onExpand | Callback function for when a treeNode is expanded or collapsed | function(expandedKeys, {expanded: bool, node}) | - |  |\n| onLoad | Callback function for when a treeNode is loaded | function(loadedKeys, {event, node}) | - |  |\n| onRightClick | Callback function for when the user right clicks a treeNode | function({event, node}) | - |  |\n| onSelect | Callback function for when the user clicks a treeNode | function(selectedKeys, e:{selected: bool, selectedNodes, node, event}) | - |  |\n\n### TreeNode props\n\n| Property | Description | Type | Default |  |\n| --- | --- | --- | --- | --- |\n| checkable | When Tree is checkable, set TreeNode display Checkbox or not | boolean | - |  |\n| disableCheckbox | Disables the checkbox of the treeNode | boolean | false |  |\n| disabled | Disables the treeNode | boolean | false |  |\n| icon | Customize icon. When you pass component, whose render will receive full TreeNode props as component props | ReactNode \\| (props) => ReactNode | - |  |\n| isLeaf | Determines if this is a leaf node(effective when `loadData` is specified). `false` will force trade TreeNode as a parent node | boolean | - |  |\n| key | Used with (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys. P.S.: It must be unique in all of treeNodes of the tree | string | (internal calculated position of treeNode) |  |\n| selectable | Set whether the treeNode can be selected | boolean | true |  |\n| title | Title | ReactNode | `---` |  |\n\n### DirectoryTree props\n\n| Property | Description | Type | Default |\n| --- | --- | --- | --- |\n| expandAction | Directory open logic, optional: false \\| `click` \\| `doubleClick` | string \\| boolean | `click` |"
            },
            "TreeSelect": {
              "description": "`TreeSelect` is similar to `Select`, but the values are provided in a tree like structure. Any data whose entries are defined in a hierarchical manner is fit to use this control. Examples of such case may include a corporate hierarchy, a directory structure, and so on.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Tree props\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| allowClear | Customize clear icon | boolean \\| { clearIcon?: ReactNode } | false | 5.8.0: Support object type |\n| autoClearSearchValue | If auto clear search input value when multiple select is selected/deselected | boolean | true |  |\n| bordered | Whether has border style | boolean | true |  |\n| defaultValue | To set the initial selected treeNode(s) | string \\| string\\[] | - |  |\n| disabled | Disabled or not | boolean | false |  |\n| popupClassName | The className of dropdown menu | string | - | 4.23.0 |\n| popupMatchSelectWidth | Determine whether the popup menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \\| number | true | 5.5.0 |\n| dropdownRender | Customize dropdown content | (originNode: ReactNode, props) => ReactNode | - |  |\n| dropdownStyle | To set the style of the dropdown menu | CSSProperties | - |  |\n| fieldNames | Customize node label, value, children field name | object | { label: `label`, value: `value`, children: `children` } | 4.17.0 |\n| filterTreeNode | Whether to filter treeNodes by input value. The value of `treeNodeFilterProp` is used for filtering by default | boolean \\| function(inputValue: string, treeNode: TreeNode) (should return boolean) | function |  |\n| getPopupContainer | To set the container of the dropdown menu. The default is to create a `div` element in `body`, you can reset it to the scrolling area and make a relative reposition. [example](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | function(triggerNode) | () => document.body |  |\n| labelInValue | Whether to embed label in value, turn the format of value from `string` to {value: string, label: ReactNode, halfChecked: string\\[]} | boolean | false |  |\n| listHeight | Config popup height | number | 256 |  |\n| loadData | Load data asynchronously. Will not load when filtering. Check FAQ for more info | function(node) | - |  |\n| maxTagCount | Max tag count to show. `responsive` will cost render performance | number \\| `responsive` | - | responsive: 4.10 |\n| maxTagPlaceholder | Placeholder for not showing tags | ReactNode \\| function(omittedValues) | - |  |\n| maxTagTextLength | Max tag text length to show | number | - |  |\n| multiple | Support multiple or not, will be `true` when enable `treeCheckable` | boolean | false |  |\n| notFoundContent | Specify content to show when no result matches | ReactNode | `Not Found` |  |\n| placeholder | Placeholder of the select input | string | - |  |\n| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft |  |\n| searchValue | Work with `onSearch` to make search value controlled | string | - |  |\n| showCheckedStrategy | The way show selected item in box when `treeCheckable` set. **Default:** just show child nodes. **`TreeSelect.SHOW_ALL`:** show all checked treeNodes (include parent treeNode). **`TreeSelect.SHOW_PARENT`:** show checked treeNodes (just show parent treeNode) | `TreeSelect.SHOW_ALL` \\| `TreeSelect.SHOW_PARENT` \\| `TreeSelect.SHOW_CHILD` | `TreeSelect.SHOW_CHILD` |  |\n| showSearch | Support search or not | boolean | single: false \\| multiple: true |  |\n| size | To set the size of the select input | `large` \\| `middle` \\| `small` | - |  |\n| status | Set validation status | 'error' \\| 'warning' | - | 4.19.0 |\n| suffixIcon | The custom suffix icon | ReactNode | `<DownOutlined />` |  |\n| switcherIcon | Customize collapse/expand icon of tree node | ReactNode \\| ((props: AntTreeNodeProps) => ReactNode) | - | renderProps: 4.20.0 |\n| tagRender | Customize tag render when `multiple` | (props) => ReactNode | - |  |\n| treeCheckable | Whether to show checkbox on the treeNodes | boolean | false |  |\n| treeCheckStrictly | Whether to check nodes precisely (in the `checkable` mode), means parent and child nodes are not associated, and it will make `labelInValue` be true | boolean | false |  |\n| treeData | Data of the treeNodes, manual construction work is no longer needed if this property has been set(ensure the Uniqueness of each value) | array&lt;{ value, title, children, \\[disabled, disableCheckbox, selectable, checkable] }> | \\[] |  |\n| treeDataSimpleMode | Enable simple mode of treeData. Changes the `treeData` schema to: \\[{id:1, pId:0, value:'1', title:\"test1\",...},...] where pId is parent node's id). It is possible to replace the default `id` and `pId` keys by providing object to `treeDataSimpleMode` | boolean \\| object&lt;{ id: string, pId: string, rootPId: string }> | false |  |\n| treeDefaultExpandAll | Whether to expand all treeNodes by default | boolean | false |  |\n| treeDefaultExpandedKeys | Default expanded treeNodes | string\\[] | - |  |\n| treeExpandAction | Tree title open logic when click, optional: false \\| `click` \\| `doubleClick` | string \\| boolean | false | 4.21.0 |\n| treeExpandedKeys | Set expanded keys | string\\[] | - |  |\n| treeIcon | Shows the icon before a TreeNode's title. There is no default style; you must set a custom style for it if set to `true` | boolean | false |  |\n| treeLoadedKeys | (Controlled) Set loaded tree nodes, work with `loadData` only | string[] | [] |  |\n| treeLine | Show the line. Ref [Tree - showLine](/components/tree/#components-tree-demo-line) | boolean \\| object | false | 4.17.0 |\n| treeNodeFilterProp | Will be used for filtering if `filterTreeNode` returns true | string | `value` |  |\n| treeNodeLabelProp | Will render as content of select | string | `title` |  |\n| value | To set the current selected treeNode(s) | string \\| string\\[] | - |  |\n| virtual | Disable virtual scroll when set to false | boolean | true | 4.1.0 |\n| onChange | A callback function, can be executed when selected treeNodes or input value change | function(value, label, extra) | - |  |\n| onDropdownVisibleChange | Called when dropdown open | function(open) | - |  |\n| onSearch | A callback function, can be executed when the search input changes | function(value: string) | - |  |\n| onSelect | A callback function, can be executed when you select a treeNode | function(value, node, extra) | - |  |\n| onTreeExpand | A callback function, can be executed when treeNode expanded | function(expandedKeys) | - |  |\n\n### Tree Methods\n\n| Name    | Description  | Version |\n| ------- | ------------ | ------- |\n| blur()  | Remove focus |         |\n| focus() | Get focus    |         |\n\n### TreeNode props\n\n> We recommend you to use `treeData` rather than `TreeNode`, to avoid the trouble of manual construction.\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| checkable | When Tree is checkable, set TreeNode display Checkbox or not | boolean | - |  |\n| disableCheckbox | Disables the checkbox of the treeNode | boolean | false |  |\n| disabled | Disabled or not | boolean | false |  |\n| isLeaf | Leaf node or not | boolean | false |  |\n| key | Required property (unless using `treeDataSimpleMode`), should be unique in the tree | string | - |  |\n| selectable | Whether can be selected | boolean | true |  |\n| title | Content showed on the treeNodes | ReactNode | `---` |  |\n| value | Will be treated as `treeNodeFilterProp` by default, should be unique in the tree | string | - |  |"
            },
            "Typography": {
              "description": "- When need to display a title or paragraph contents in Articles/Blogs/Notes.\n- When you need copyable/editable/ellipsis texts.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Typography.Text\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| code | Code style | boolean | false |  |\n| copyable | Whether to be copyable, customize it via setting an object | boolean \\| [copyable](#copyable) | false | [copyable](#copyable) |\n| delete | Deleted line style | boolean | false |  |\n| disabled | Disabled content | boolean | false |  |\n| editable | If editable. Can control edit state when is object | boolean \\| [editable](#editable) | false | [editable](#editable) |\n| ellipsis | Display ellipsis when text overflows, can't configure expandable、rows and onExpand by using object. Diff with Typography.Paragraph, Text do not have 100% width style which means it will fix width on the first ellipsis. If you want to have responsive ellipsis, please set width manually | boolean \\| [Omit<ellipsis, 'expandable' \\| 'rows' \\| 'onExpand'>](#ellipsis) | false | [ellipsis](#ellipsis) |\n| keyboard | Keyboard style | boolean | false | 4.3.0 |\n| mark | Marked style | boolean | false |  |\n| onClick | Set the handler to handle click event | (event) => void | - |  |\n| strong | Bold style | boolean | false |  |\n| italic | Italic style | boolean | false | 4.16.0 |\n| type | Content type | `secondary` \\| `success` \\| `warning` \\| `danger` | - | success: 4.6.0 |\n| underline | Underlined style | boolean | false |  |\n\n### Typography.Title\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| code | Code style | boolean | false |  |\n| copyable | Whether to be copyable, customize it via setting an object | boolean \\| [copyable](#copyable) | false | [copyable](#copyable) |\n| delete | Deleted line style | boolean | false |  |\n| disabled | Disabled content | boolean | false |  |\n| editable | If editable. Can control edit state when is object | boolean \\| [editable](#editable) | false | [editable](#editable) |\n| ellipsis | Display ellipsis when text overflows, can configure rows and expandable by using object | boolean \\| [ellipsis](#ellipsis) | false | [ellipsis](#ellipsis) |\n| level | Set content importance. Match with `h1`, `h2`, `h3`, `h4`, `h5` | number: 1, 2, 3, 4, 5 | 1 | 5: 4.6.0 |\n| mark | Marked style | boolean | false |  |\n| onClick | Set the handler to handle click event | (event) => void | - |  |\n| italic | Italic style | boolean | false | 4.16.0 |\n| type | Content type | `secondary` \\| `success` \\| `warning` \\| `danger` | - | success: 4.6.0 |\n| underline | Underlined style | boolean | false |  |\n\n### Typography.Paragraph\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| code | Code style | boolean | false |  |\n| copyable | Whether to be copyable, customize it via setting an object | boolean \\| [copyable](#copyable) | false | [copyable](#copyable) |\n| delete | Deleted line style | boolean | false |  |\n| disabled | Disabled content | boolean | false |  |\n| editable | If editable. Can control edit state when is object | boolean \\| [editable](#editable) | false | [editable](#editable) |\n| ellipsis | Display ellipsis when text overflows, can configure rows and expandable by using object | boolean \\| [ellipsis](#ellipsis) | false | [ellipsis](#ellipsis) |\n| mark | Marked style | boolean | false |  |\n| onClick | Set the handler to handle click event | (event) => void | - |  |\n| strong | Bold style | boolean | false |  |\n| italic | Italic style | boolean | false | 4.16.0 |\n| type | Content type | `secondary` \\| `success` \\| `warning` \\| `danger` | - | success: 4.6.0 |\n| underline | Underlined style | boolean | false |  |\n\n### copyable\n\n    {\n      text: string,\n      onCopy: function(event),\n      icon: ReactNode,\n      tooltips: false | [ReactNode, ReactNode],\n      format: 'text/plain' | 'text/html',\n    }\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| format | The Mime Type of the text | 'text/plain' \\| 'text/html' | - | 4.21.0 |\n| icon | Custom copy icon: \\[copyIcon, copiedIcon] | \\[ReactNode, ReactNode] | - | 4.6.0 |\n| text | The text to copy | string | - |  |\n| tooltips | Custom tooltip text, hide when it is false | \\[ReactNode, ReactNode] | \\[`Copy`, `Copied`] | 4.4.0 |\n| onCopy | Called when copied text | function | - |  |\n\n### editable\n\n    {\n      icon: ReactNode,\n      tooltip: boolean | ReactNode,\n      editing: boolean,\n      maxLength: number,\n      autoSize: boolean | { minRows: number, maxRows: number },\n      text: string,\n      onChange: function(string),\n      onCancel: function,\n      onStart: function,\n      onEnd: function,\n      triggerType: ('icon' | 'text')[],\n      enterIcon: ReactNode,\n    }\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| autoSize | `autoSize` attribute of textarea | boolean \\| { minRows: number, maxRows: number } | - | 4.4.0 |\n| editing | Whether to be editable | boolean | false |  |\n| icon | Custom editable icon | ReactNode | &lt;EditOutlined /> | 4.6.0 |\n| maxLength | `maxLength` attribute of textarea | number | - | 4.4.0 |\n| tooltip | Custom tooltip text, hide when it is false | boolean \\| ReactNode | `Edit` | 4.6.0 |\n| text | Edit text, specify the editing content instead of using the children implicitly | string | - | 4.24.0 |\n| onChange | Called when input at textarea | function(value: string) | - |  |\n| onCancel | Called when type ESC to exit editable state | function | - |  |\n| onStart | Called when enter editable state | function | - |  |\n| onEnd | Called when type ENTER to exit editable state | function | - | 4.14.0 |\n| triggerType | Edit mode trigger - icon, text or both (not specifying icon as trigger hides it) | Array&lt;`icon`\\|`text`> | \\[`icon`] |  |\n| enterIcon | Custom \"enter\" icon in the edit field (passing `null` removes the icon) | ReactNode | `<EnterOutlined />` | 4.17.0 |\n\n### ellipsis\n\n    {\n      rows: number,\n      expandable: boolean,\n      suffix: string,\n      symbol: ReactNode,\n      tooltip: boolean | ReactNode | TooltipProps,\n      onExpand: function(event),\n      onEllipsis: function(ellipsis),\n    }\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| expandable | Whether to be expandable | boolean | - |  |\n| rows | Max rows of content | number | - |  |\n| suffix | Suffix of ellipsis content | string | - |  |\n| symbol | Custom description of ellipsis | ReactNode | `Expand` |  |\n| tooltip | Show tooltip when ellipsis | boolean \\| ReactNode \\| [TooltipProps](/components/tooltip/#api) | - | 4.11.0 |\n| onEllipsis | Called when enter or leave ellipsis state | function(ellipsis) | - | 4.2.0 |\n| onExpand | Called when expand content | function(event) | - |  |"
            },
            "Upload": {
              "description": "Uploading is the process of publishing information (web pages, text, pictures, video, etc.) to a remote server via a web page or upload tool.\n\n- When you need to upload one or more files.\n- When you need to show the process of uploading.\n- When you need to upload files by dragging and dropping.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| accept | File types that can be accepted. See [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) | string | - |  |\n| action | Uploading URL | string \\| (file) => Promise&lt;string> | - |  |\n| beforeUpload | Hook function which will be executed before uploading. Uploading will be stopped with `false` or a rejected Promise returned. When returned value is `Upload.LIST_IGNORE`, the list of files that have been uploaded will ignore it. **Warning:this function is not supported in IE9** | (file, fileList) => boolean \\| Promise&lt;File> \\| `Upload.LIST_IGNORE` | - |  |\n| customRequest | Override for the default xhr behavior allowing for additional customization and the ability to implement your own XMLHttpRequest | function | - |  |\n| data | Uploading extra params or function which can return uploading extra params | object \\| (file) => object \\| Promise&lt;object> | - |  |\n| defaultFileList | Default list of files that have been uploaded | object\\[] | - |  |\n| directory | Support upload whole directory ([caniuse](https://caniuse.com/#feat=input-file-directory)) | boolean | false |  |\n| disabled | Disable upload button | boolean | false |  |\n| fileList | List of files that have been uploaded (controlled). Here is a common issue [#2423](https://github.com/@private-basic-components/@private-basic-components/issues/2423) when using it | [UploadFile](#uploadfile)\\[] | - |  |\n| headers | Set request headers, valid above IE10 | object | - |  |\n| iconRender | Custom show icon | (file: UploadFile, listType?: UploadListType) => ReactNode | - |  |\n| isImageUrl | Customize if render &lt;img /> in thumbnail | (file: UploadFile) => boolean | [(inside implementation)](https://github.com/@private-basic-components/@private-basic-components/blob/4ad5830eecfb87471cd8ac588c5d992862b70770/components/upload/utils.tsx#L47-L68) |  |\n| itemRender | Custom item of uploadList | (originNode: ReactElement, file: UploadFile, fileList: object\\[], actions: { download: function, preview: function, remove: function }) => React.ReactNode | - | 4.16.0 |\n| listType | Built-in stylesheets, support for four types: `text`, `picture`, `picture-card` or `picture-circle` | string | `text` | `picture-circle`(5.2.0+) |\n| maxCount | Limit the number of uploaded files. Will replace current one when `maxCount` is `1` | number | - | 4.10.0 |\n| method | The http method of upload request | string | `post` |  |\n| multiple | Whether to support selected multiple files. `IE10+` supported. You can select multiple files with CTRL holding down while multiple is set to be true | boolean | false |  |\n| name | The name of uploading file | string | `file` |  |\n| openFileDialogOnClick | Click open file dialog | boolean | true |  |\n| previewFile | Customize preview file logic | (file: File \\| Blob) => Promise&lt;dataURL: string> | - |  |\n| progress | Custom progress bar | [ProgressProps](/components/progress/#api) (support `type=\"line\"` only) | { strokeWidth: 2, showInfo: false } | 4.3.0 |\n| showUploadList | Whether to show default upload list, could be an object to specify `showPreviewIcon`, `showRemoveIcon`, `showDownloadIcon`, `removeIcon` and `downloadIcon` individually | boolean \\| { showPreviewIcon?: boolean, showDownloadIcon?: boolean, showRemoveIcon?: boolean, previewIcon?: ReactNode \\| (file: UploadFile) => ReactNode, removeIcon?: ReactNode \\| (file: UploadFile) => ReactNode, downloadIcon?: ReactNode \\| (file: UploadFile) => ReactNode } | true | function: 4.7.0 |\n| withCredentials | The ajax upload with cookie sent | boolean | false |  |\n| onChange | A callback function, can be executed when uploading state is changing. It will trigger by every uploading phase. see [onChange](#onchange) | function | - |  |\n| onDrop | A callback function executed when files are dragged and dropped into the upload area | (event: React.DragEvent) => void | - | 4.16.0 |\n| onDownload | Click the method to download the file, pass the method to perform the method logic, and do not pass the default jump to the new TAB | function(file): void | (Jump to new TAB) |  |\n| onPreview | A callback function, will be executed when the file link or preview icon is clicked | function(file) | - |  |\n| onRemove | A callback function, will be executed when removing file button is clicked, remove event will be prevented when the return value is false or a Promise which resolve(false) or reject | function(file): boolean \\| Promise | - |  |\n\n### UploadFile\n\nExtends File with additional props.\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| crossOrigin | CORS settings attributes | `'anonymous'` \\| `'use-credentials'` \\| `''` | - | 4.20.0 |\n| name | File name | string | - | - |\n| percent | Upload progress percent | number | - | - |\n| status | Upload status. Show different style when configured | `error` \\| `done` \\| `uploading` \\| `removed` | - | - |\n| thumbUrl | Thumb image url | string | - | - |\n| uid | unique id. Will auto-generate when not provided | string | - | - |\n| url | Download url | string | - | - |\n\n### onChange\n\n> 💡 The function will be called when uploading is in progress, completed, or failed.\n\nWhen uploading state change, it returns:\n\n```jsx\n{\n  file: { /* ... */ },\n  fileList: [ /* ... */ ],\n  event: { /* ... */ },\n}\n```\n\n1. `file` File object for the current operation.\n\n   ```jsx\n   {\n      uid: 'uid',      // unique identifier, negative is recommended, to prevent interference with internally generated id\n      name: 'xx.png',   // file name\n      status: 'done' | 'uploading' | 'error' | 'removed', // Intercepted file by beforeUpload doesn't have a status field.\n      response: '{\"status\": \"success\"}', // response from server\n      linkProps: '{\"download\": \"image\"}', // additional HTML props of file link\n      xhr: 'XMLHttpRequest{ ... }', // XMLHttpRequest Header\n   }\n   ```\n\n2. `fileList` current list of files\n\n3. `event` response from the server, including uploading progress, supported by advanced browsers."
            },
            "Watermark": {
              "description": "- Use when the page needs to be watermarked to identify the copyright.\n- Suitable for preventing information theft.",
              "api": "Common props ref:[Common props](/docs/react/common-props)\n\n### Watermark\n\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| width | The width of the watermark, the default value of `content` is its own width | number | 120 |  |\n| height | The height of the watermark, the default value of `content` is its own height | number | 64 |  |\n| rotate | When the watermark is drawn, the rotation Angle, unit `°` | number | -22 |  |\n| zIndex | The z-index of the appended watermark element | number | 9 |  |\n| image | Image source, it is recommended to export 2x or 3x image, high priority (support base64 format) | string | - |  |\n| content | Watermark text content | string \\| string[] | - |  |\n| font | Text style | [Font](#font) | [Font](#font) |  |\n| gap | The spacing between watermarks | \\[number, number\\] | \\[100, 100\\] |  |\n| offset | The offset of the watermark from the upper left corner of the container. The default is `gap/2` | \\[number, number\\] | \\[gap\\[0\\]/2, gap\\[1\\]/2\\] |  |\n\n### Font\n\n<!-- prettier-ignore -->\n| Property | Description | Type | Default | Version |\n| --- | --- | --- | --- | --- |\n| color | font color | [CanvasFillStrokeStyles.fillStyle](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/fillStyle) | rgba(0,0,0,.15) |  |\n| fontSize | font size | number | 16 |  |\n| fontWeight | font weight | `normal` \\| `light` \\| `weight` \\| number | normal |  |\n| fontFamily | font family | string | sans-serif |  |\n| fontStyle | font style  | `none` \\| `normal` \\| `italic` \\| `oblique` | normal |  |\n| textAlign | specify the text alignment direction  | [CanvasTextAlign](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/textAlign) | `center` | 5.10.0 |"
            }
          }
        }
      }
    ]
  },
  {
    "title": "Landing Page Codegen",
    "description": "Code generator based on Page UI for landing page",
    "fullStack": "React",
    "guides": ["Generate a landing page for a AI SaaS product"],
    "model": "gpt-4o",
    "codeRendererUrl": "http://localhost:6010",
    "rules": [
      {
        "type": "styles",
        "description": "Define the rules for generating styles",
        "prompt": "Styles must be written using tailwindcss, and the component must be responsive."
      },
      {
        "type": "private-components",
        "description": "Define the project structure for the landing page",
        "docs": {
          "pageui": {
            "About": {
              "description": "The About section provides a sleek, modern way to showcase your company information, mission, vision, and key statistics. These components are designed to build trust and communicate your core values to your audience. ## About Section The `LandingAboutSection` component displays a heading, description, and an image side by side, perfect for introducing your company or team.",
              "api": "<Usage>\n```jsx\nimport { LandingAboutSection } from '@/components/landing/about/LandingAboutSection';\n```\n\n```jsx\n<LandingAboutSection\n  title=\"About Us\"\n  description=\"We are committed to creating a safe and supportive environment where you can explore your thoughts and feelings, develop coping strategies, and achieve your mental health goals.\"\n  imageSrc=\"/static/images/backdrop-1.webp\"\n  imageAlt=\"About us image\"\n/>\n```\n</Usage>\n\n<Examples>\n### About Section with Background\n\n\n\n```jsx\nimport { LandingAboutSection } from '@/components/landing/about/LandingAboutSection';\n\n<LandingAboutSection\n  title=\"Our Story\"\n  description=\"Founded in 2010, we've grown from a small team of passionate developers to a global agency serving clients worldwide. Our journey has been defined by innovation, quality, and a commitment to excellence in everything we do.\"\n  imageSrc=\"/static/images/backdrop-2.webp\"\n  imageAlt=\"Team working together\"\n  withBackground={true}\n  variant=\"secondary\"\n/>\n```\n\n\n\n### About Section with Background Glow\n\n\n\n```jsx\nimport { LandingAboutSection } from '@/components/landing/about/LandingAboutSection';\n\n<LandingAboutSection\n  title=\"About Our Platform\"\n  description=\"Our platform combines cutting-edge technology with user-friendly design to create a seamless experience for our users. We're constantly innovating and improving to provide the best possible service.\"\n  imageSrc=\"/static/images/backdrop-3.webp\"\n  imageAlt=\"Platform showcase\"\n  withBackground={true}\n  withBackgroundGlow={true}\n  variant=\"primary\"\n/>\n```\n\n\n\n### Vision & Mission Section\n\nThe `LandingVisionMissionSection` component displays your company's vision and mission statements in a clean, organized layout.\n\n\n\n```jsx\nimport { LandingVisionMissionSection } from '@/components/landing/about/LandingVisionMissionSection';\n\n<LandingVisionMissionSection\n  title=\"Empowering Global Mental Health Access\"\n  visionTitle=\"OUR VISION\"\n  visionDescription=\"To be the leading mental health platform, providing accessible, compassionate, and innovative care for emotional and mental well-being worldwide.\"\n  missionTitle=\"OUR MISSION\"\n  missionDescription=\"To support individuals in achieving mental and emotional balance through tailored therapy, education, ensuring everyone has access to professional care.\"\n/>\n```\n\n\n\n### Vision & Mission with Background\n\n\n\n```jsx\nimport { LandingVisionMissionSection } from '@/components/landing/about/LandingVisionMissionSection';\n\n<LandingVisionMissionSection\n  title=\"Our Core Values\"\n  visionTitle=\"WHY WE EXIST\"\n  visionDescription=\"To transform how businesses approach digital solutions, making advanced technology accessible to companies of all sizes.\"\n  missionTitle=\"HOW WE WORK\"\n  missionDescription=\"By combining innovative design thinking with cutting-edge development practices to create solutions that are both powerful and user-friendly.\"\n  withBackground={true}\n  variant=\"secondary\"\n/>\n```\n\n\n\n### Complete About Page Example\n\nThis example combines the About Section, Vision & Mission Section, and Stats Section to create a comprehensive About page.\n\n\n\n```jsx\nimport { LandingAboutSection } from '@/components/landing/about/LandingAboutSection';\nimport { LandingVisionMissionSection } from '@/components/landing/about/LandingVisionMissionSection';\nimport { LandingStatsSection } from '@/components/landing/stats/LandingStatsSection';\n\n<div className=\"flex flex-col w-full\">\n  <LandingAboutSection\n    title=\"About Our Agency\"\n    description=\"We are a full-service digital agency specializing in web development, design, and marketing. With over a decade of experience, we've helped hundreds of clients achieve their goals and grow their online presence.\"\n    imageSrc=\"/static/images/backdrop-4.webp\"\n    imageAlt=\"Agency team photo\"\n  />\n\n  <LandingStatsSection\n    columnsDesktop={3}\n    hasBorders={true}\n    stats={[\n      { value: '150+', description: 'Over 150 projects successfully delivered.' },\n      { value: '9', label: 'members', description: 'Our core team consists of 9 experts.' },\n      { value: '300+', label: 'creative freelancers', description: 'We collaborate with 300+ creative freelancers.' },\n      { value: '25+', label: 'awards & featured', description: 'Recognized with 25+ awards and featured in industry publications.' },\n      { value: '10+', label: 'years experience', description: 'Bringing 10+ years of design experience to every project.' },\n      { value: '$25B+', label: 'revenue', description: 'Our work has contributed to over $25 billion in revenue.' }\n    ]}\n  />\n\n  <LandingVisionMissionSection\n    title=\"Our Guiding Principles\"\n    visionTitle=\"VISION\"\n    visionDescription=\"To be the most trusted partner for businesses seeking to transform their digital presence and achieve meaningful growth.\"\n    missionTitle=\"MISSION\"\n    missionDescription=\"To deliver exceptional digital solutions that drive real business results through a combination of creativity, technical excellence, and strategic thinking.\"\n    withBackground={true}\n    withBackgroundGlow={true}\n    variant=\"primary\"\n  />\n</div>\n```\n\n\n\n### Complete example with custom stats\n\n\n\n```jsx\nimport { LandingAboutSection } from '@/components/landing/about/LandingAboutSection';\nimport { LandingStatItem } from '@/components/landing/stats/LandingStatItem';\n\n<div className=\"flex flex-col w-full\">\n  <LandingAboutSection\n    title=\"About Us\"\n    description=\"We're a team of passionate designers and developers building exceptional digital experiences.\"\n    imageSrc=\"/static/images/backdrop-5.webp\"\n    imageAlt=\"Our team\"\n  />\n\n  <div className=\"py-8\">\n    <div className=\"container-wide px-6\">\n      <h2 className=\"w-full text-3xl font-semibold mb-6 text-left\">Key Statistics</h2>\n      <div className=\"grid grid-cols-2 md:grid-cols-3\">\n        <LandingStatItem\n          value=\"150+\"\n          label=\"projects\"\n          description=\"Successfully completed projects\"\n          variant=\"primary\"\n        />\n        <LandingStatItem\n          value=\"9\"\n          label=\"members\"\n          description=\"Dedicated members\"\n          variant=\"primary\"\n        />\n        <LandingStatItem\n          value=\"10+\"\n          label=\"years\"\n          description=\"Years of industry experience\"\n          variant=\"primary\"\n        />\n      </div>\n    </div>\n  </div>\n</div>\n```\n\n\n</Examples>\n\n<PropsReference>\n### LandingAboutSection Props\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **title** <Tippy>The main heading for the About section</Tippy> | `string` | No | `'About Us'` |\n| **titleComponent** <Tippy>Custom component to replace the default title</Tippy> | `React.ReactNode` | No | `-` |\n| **description** <Tippy>The description text for the About section</Tippy> | `string \\| React.ReactNode` | No | `-` |\n| **descriptionComponent** <Tippy>Custom component to replace the default description</Tippy> | `React.ReactNode` | No | `-` |\n| **imageSrc** <Tippy>The URL for the image to display</Tippy> | `string` | No | `'/static/images/about-image.webp'` |\n| **imageAlt** <Tippy>The alt text for the image</Tippy> | `string` | No | `'About us image'` |\n| **textPosition** <Tippy>The alignment of the text content</Tippy> | `'center' \\| 'left'` | No | `'left'` |\n| **withBackground** <Tippy>Whether to display a background color</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Whether to display a background glow effect</Tippy> | `boolean` | No | `false` |\n| **variant** <Tippy>Visual style variant of the section</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **backgroundGlowVariant** <Tippy>Visual style variant of the background glow</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **className** <Tippy>Additional classes to apply to the section wrapper</Tippy> | `string` | No | `-` |\n| **innerClassName** <Tippy>Additional classes to apply to the inner container</Tippy> | `string` | No | `-` |\n\n### LandingVisionMissionSection Props\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **title** <Tippy>The main heading for the section</Tippy> | `string` | No | `-` |\n| **titleComponent** <Tippy>Custom component to replace the default title</Tippy> | `React.ReactNode` | No | `-` |\n| **visionTitle** <Tippy>The heading for the vision section</Tippy> | `string` | No | `-` |\n| **visionDescription** <Tippy>The description text for the vision</Tippy> | `string \\| React.ReactNode` | No | `-` |\n| **missionTitle** <Tippy>The heading for the mission section</Tippy> | `string` | No | `-` |\n| **missionDescription** <Tippy>The description text for the mission</Tippy> | `string \\| React.ReactNode` | No | `-` |\n| **variant** <Tippy>Visual style variant of the section</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **withBackground** <Tippy>Whether to display a background color</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Whether to display a background glow effect</Tippy> | `boolean` | No | `false` |\n| **backgroundGlowVariant** <Tippy>Visual style variant of the background glow</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **textPosition** <Tippy>The alignment of the text content</Tippy> | `'center' \\| 'left'` | No | `'left'` |\n| **className** <Tippy>Additional classes to apply to the section wrapper</Tippy> | `string` | No | `-` |\n| **innerClassName** <Tippy>Additional classes to apply to the inner container</Tippy> | `string` | No | `-` |\n| **children** <Tippy>Additional content to display below the vision and mission content</Tippy> | `React.ReactNode` | No | `-` |\n</PropsReference>"
            },
            "AppstoreButton": {
              "description": "This component displays app store download buttons with proper branding. It supports multiple app stores and black/white variants.",
              "api": "<Usage>\n```jsx\nimport { LandingAppStoreButton } from '@/components/landing/app-store-button/LandingAppStoreButton';\n```\n\n```jsx\n<LandingAppStoreButton appStore=\"ios-appstore\" />\n```\n</Usage>\n\n<Examples>\n### Use as Link\n\n\n\n```jsx\n<LandingAppStoreButton appStore=\"ios-appstore\" asChild>\n  <a href=\"https://www.apple.com/app-store\">Download</a>\n</LandingAppStoreButton>\n```\n\n\n\n### Use with onClick Event\nYou can also use the onClick event handler for custom interactions.\n\n\n\n```jsx\n<LandingAppStoreButton\n  appStore=\"google-playstore\"\n  onClick={() => console.log('Button clicked!')}\n/>\n```\n\n\n\n### Explicit Theme Variant\nYou can explicitly set the button variant to white or black, overriding the default theme.\n\n\n\n```jsx\n<div className=\"flex flex-wrap gap-4\">\n  <LandingAppStoreButton appStore=\"ios-appstore\" variant=\"white\" />\n  <LandingAppStoreButton appStore=\"ios-appstore\" variant=\"black\" />\n</div>\n```\n\n\n\n### Different Sizes\nThe button supports different sizes, corresponding to regular `<Button>` component sizes.\n\n\n\n```jsx\n<div className=\"flex flex-wrap gap-4\">\n  <LandingAppStoreButton appStore=\"ios-appstore\" size=\"sm\" />\n  <LandingAppStoreButton appStore=\"ios-appstore\" size=\"default\" />\n  <LandingAppStoreButton appStore=\"ios-appstore\" size=\"lg\" />\n  <LandingAppStoreButton appStore=\"ios-appstore\" size=\"xl\" />\n</div>\n```\n\n\n\n### Custom Styling\nYou can add custom styling using the className prop.\n\n\n\n```jsx\n<LandingAppStoreButton\n  appStore=\"ios-appstore\"\n  className=\"shadow-lg hover:shadow-xl transition-shadow duration-300\"\n/>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name | Prop Type | Required | Default |\n| --------- | --------- | -------- | ------- |\n| **appStore** <Tippy>The app store type to display.</Tippy> | `'ios-appstore'` ǀ `'mac-appstore'` ǀ `'microsoft-store'` ǀ `'google-playstore'` | Yes | - |\n| **variant** <Tippy>Explicitly set the button theme variant.</Tippy> | `'white'` ǀ `'black'` | No | `'black'` |\n| **size** <Tippy>Size of the button.</Tippy> | `'default'` ǀ `'sm'` ǀ `'lg'` ǀ `'xl'` | No | `'default'` |\n| **onClick** <Tippy>Event handler for the button click.</Tippy> | `(event: React.MouseEvent<HTMLButtonElement>) => void` | No | - |\n| **asChild** <Tippy>When true, the component will render its child component instead of a button element.</Tippy> | `boolean` | No | `false` |\n| **className** <Tippy>Additional CSS classes to apply to the button.</Tippy> | `string` | No | `''` |\n</PropsReference>"
            },
            "Band": {
              "description": "Use this component to 'break' the layout flow of your landing page and either present social proof or showcase the value proposition of the product. With this component you display a full-width, brand-colored section that displays a title, description and some product logos or icons.",
              "api": "<Usage>\n```jsx\nimport { LandingBandSection } from '@/components/landing/LandingBand';\nimport { ChromeIcon, FigmaIcon, GithubIcon } from 'lucide-react';\n```\n\n```jsx\n<LandingBandSection\n  title={'20-100h'}\n  description={'Saved on development by using Shipixen'}\n  supportingComponent={\n    <>\n      <ChromeIcon className=\"w-12 h-12\" />\n      <FigmaIcon className=\"w-12 h-12\" />\n      <GithubIcon className=\"w-12 h-12\" />\n    </>\n  }\n/>\n```\n</Usage>\n\n<Examples>\n### With Social Proof\n\n\n\n```jsx\nimport { LandingBandSection } from '@/components/landing/LandingBand';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\n\nconst avatarItems = [\n  {\n    imageSrc: '/static/images/people/1.webp',\n    name: 'John Doe',\n  },\n  {\n    imageSrc: '/static/images/people/2.webp',\n    name: 'Jane Doe',\n  },\n  {\n    imageSrc: '/static/images/people/3.webp',\n    name: 'Alice Doe',\n  },\n]\n\n<LandingBandSection\n  title='4.9/5 stars'\n  description='Our customers love our product'\n  supportingComponent={\n    <LandingSocialProof\n      showRating\n      numberOfUsers={99}\n      avatarItems={avatarItems}\n    />\n  }\n/>\n```\n\n\n\n### Customization\n\n\n\n```jsx\nimport { ChromeIcon, FigmaIcon, GithubIcon } from 'lucide-react';\nimport { LandingBandSection } from '@/components/landing/LandingBand';\n\n<LandingBandSection\n  variant=\"secondary\"\n  title=\"20-100h\"\n  description=\"Saved on design\"\n  supportingComponent={\n    <>\n      <ChromeIcon className=\"w-12 h-12\" />\n      <FigmaIcon className=\"w-12 h-12\" />\n      <GithubIcon className=\"w-12 h-12\" />\n    </>\n  }\n/>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                             | Prop Type                   | Required | Default     |\n| ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | -------- | ----------- |\n| **children** <Tippy>React nodes to be rendered within the component (below the text on smaller screen, right side on larger).</Tippy> | `React.ReactNode`           | No       | -           |\n| **title** <Tippy>The title to display. Can be a string or a React node for custom components</Tippy>                                  | `string \\| React.ReactNode` | No       | -           |\n| **titleComponent** <Tippy>Custom React component to use instead of the default title</Tippy>                                          | `React.ReactNode`           | No       | -           |\n| **description** <Tippy>Description text or a React node for custom description components</Tippy>                                     | `string \\| React.ReactNode` | No       | -           |\n| **descriptionComponent** <Tippy>Custom React component to use instead of the default description</Tippy>                              | `React.ReactNode`           | No       | -           |\n| **supportingComponent** <Tippy>A React component to support the main content, such as images or illustrations</Tippy>                 | `React.ReactNode`           | No       | -           |\n| **withBackground** <Tippy>Whether the section should have a background color</Tippy>                                                  | `boolean`                   | No       | `true`      |\n| **variant** <Tippy>Color variant of the background</Tippy>                                                                            | `'primary' \\| 'secondary'`  | No       | `'primary'` |\n</PropsReference>"
            },
            "BentoGrid": {
              "description": "The `LandingBentoGridSection` component is a flexible container for creating visually appealing grid layouts for landing pages. It provides a grid layout with customizable columns and rows, and can be used to display a variety of content types. It can contain generic items or specialized Bento Grid items as children. These specialized items extend the basic bento grid with pre-designed layouts for displaying tech specs, feature highlights, and product information.",
              "api": "<Usage>\n```jsx\nimport { LandingBentoGridSection } from '@/components/landing/bento-grid/LandingBentoGridSection';\nimport { LandingBentoGridIconItem } from '@/components/landing/bento-grid/LandingBentoGridIconItem';\nimport { LandingBentoGridNumberItem } from '@/components/landing/bento-grid/LandingBentoGridNumberItem';\nimport { LandingBentoGridImageItem } from '@/components/landing/bento-grid/LandingBentoGridImageItem';\n```\n\n```jsx\n<LandingBentoGridSection\n  title=\"Processor Specifications\"\n  description=\"Technical details of our latest chipset\"\n>\n  <LandingBentoGridIconItem\n    icon={<SparklesIcon className=\"w-12 h-12\" />}\n    bottomText=\"High-speed I/O\"\n  />\n  <LandingBentoGridNumberItem\n    topText=\"Up to\"\n    number=\"20%\"\n    bottomText=\"faster CPU\"\n  />\n  <LandingBentoGridImageItem\n    topText=\"Data-center-class\"\n    imageSrc=\"/static/images/backdrop-1.webp\"\n    imageAlt=\"Data-center-class\"\n    bottomText=\"performance per watt\"\n    colSpan={2}\n    rowSpan={2}\n  />\n\n  {/* more items */}\n</LandingBentoGridSection>\n```\n</Usage>\n\n<Examples>\n### Icon-based Grid Items\n\n\n\n```jsx\nimport { LandingBentoGridSection } from '@/components/landing/bento-grid/LandingBentoGridSection';\nimport { LandingBentoGridIconItem } from '@/components/landing/bento-grid/LandingBentoGridIconItem';\n\n<LandingBentoGridSection\n  title=\"Technical Specifications\"\n  description=\"Cutting-edge technology in every component\"\n>\n  <LandingBentoGridIconItem\n    icon={<SparklesIcon className=\"w-10 h-10\" />}\n    bottomText=\"High-speed I/O\"\n  />\n  <LandingBentoGridIconItem\n    topText=\"Dedicated\"\n    icon={<LayersIcon className=\"w-10 h-10\" />}\n    bottomText=\"media engine\"\n  />\n  <LandingBentoGridIconItem\n    topText=\"Advanced\"\n    icon={<LineChartIcon className=\"w-10 h-10\" />}\n    bottomText=\"process technology\"\n  />\n  <LandingBentoGridIconItem\n    icon={<BatteryIcon className=\"w-10 h-10\" />}\n    bottomText=\"More battery\"\n    topText=\"More power\"\n  />\n</LandingBentoGridSection>\n```\n\n\n\n### Number-based Grid Items\n\n\n\n```jsx\nimport { LandingBentoGridSection } from '@/components/landing/bento-grid/LandingBentoGridSection';\nimport { LandingBentoGridNumberItem } from '@/components/landing/bento-grid/LandingBentoGridNumberItem';\n\n<LandingBentoGridSection\n  title=\"Performance Metrics\"\n  description=\"Benchmark results for our latest generation\"\n>\n  <LandingBentoGridNumberItem\n    topText=\"Up to\"\n    number=\"20%\"\n    bottomText=\"faster CPU\"\n  />\n  <LandingBentoGridNumberItem\n    topText=\"Up to\"\n    number=\"30%\"\n    bottomText=\"faster GPU\"\n  />\n  <LandingBentoGridNumberItem\n    number=\"60 billion\"\n    bottomText=\"transistors\"\n    topText=\"Over\"\n  />\n  <LandingBentoGridNumberItem\n    topText=\"Up to\"\n    number=\"800 GB/s\"\n    bottomText=\"Memory bandwidth\"\n    colSpan={4}\n  />\n</LandingBentoGridSection>\n```\n\n\n\n### Image-based Grid Items\n\n\n\n```jsx\nimport { LandingBentoGridSection } from '@/components/landing/bento-grid/LandingBentoGridSection';\nimport { LandingBentoGridImageItem } from '@/components/landing/bento-grid/LandingBentoGridImageItem';\n\n<LandingBentoGridSection\n  title=\"Product Gallery\"\n  description=\"Our latest innovations in images\"\n>\n  <LandingBentoGridImageItem\n    imageSrc=\"/static/images/backdrop-1.webp\"\n    bottomText=\"Next-generation display\"\n    colSpan={2}\n  />\n  <LandingBentoGridImageItem\n    topText=\"Pro Camera\"\n    imageSrc=\"/static/images/backdrop-2.webp\"\n    bottomText=\"128MP\"\n  />\n  <LandingBentoGridImageItem\n    topText=\"Redesigned\"\n    imageSrc=\"/static/images/backdrop-3.webp\"\n    bottomText=\"Architecture\"\n  />\n</LandingBentoGridSection>\n```\n\n\n</Examples>\n\n<PropsReference>\n### LandingBentoGridIconItem Props\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **variant** <Tippy>Color variant for the item</Tippy> | `'default' \\| 'primary' \\| 'secondary'` | No | `'default'` |\n| **topText** <Tippy>Text displayed above the icon</Tippy> | `string` | No | - |\n| **topTextComponent** <Tippy>Custom component for top text</Tippy> | `React.ReactNode` | No | - |\n| **icon** <Tippy>Icon to display in the center</Tippy> | `React.ReactNode` | No | - |\n| **bottomText** <Tippy>Text displayed below the icon</Tippy> | `string` | No | - |\n| **bottomTextComponent** <Tippy>Custom component for bottom text</Tippy> | `React.ReactNode` | No | - |\n| **colSpan** <Tippy>Number of columns the item should span</Tippy> | `1 \\| 2 \\| 3 \\| 4` | No | `1` |\n| **rowSpan** <Tippy>Number of rows the item should span</Tippy> | `1 \\| 2 \\| 3` | No | `1` |\n| **className** <Tippy>Additional CSS classes</Tippy> | `string` | No | `''` |\n| **href** <Tippy>Link URL to make the item clickable</Tippy> | `string` | No | - |\n| **asChild** <Tippy>Whether to render children as the main component</Tippy> | `boolean` | No | `false` |\n\n### LandingBentoGridNumberItem Props\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **variant** <Tippy>Color variant for the item</Tippy> | `'default' \\| 'primary' \\| 'secondary'` | No | `'default'` |\n| **topText** <Tippy>Text displayed above the number</Tippy> | `string` | No | - |\n| **topTextComponent** <Tippy>Custom component for top text</Tippy> | `React.ReactNode` | No | - |\n| **number** <Tippy>Number or text to display in the center</Tippy> | `string \\| number` | No | - |\n| **bottomText** <Tippy>Text displayed below the number</Tippy> | `string` | No | - |\n| **bottomTextComponent** <Tippy>Custom component for bottom text</Tippy> | `React.ReactNode` | No | - |\n| **colSpan** <Tippy>Number of columns the item should span</Tippy> | `1 \\| 2 \\| 3 \\| 4` | No | `1` |\n| **rowSpan** <Tippy>Number of rows the item should span</Tippy> | `1 \\| 2 \\| 3` | No | `1` |\n| **className** <Tippy>Additional CSS classes</Tippy> | `string` | No | `''` |\n| **href** <Tippy>Link URL to make the item clickable</Tippy> | `string` | No | - |\n| **asChild** <Tippy>Whether to render children as the main component</Tippy> | `boolean` | No | `false` |\n\n### LandingBentoGridImageItem Props\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **variant** <Tippy>Color variant for the item</Tippy> | `'default' \\| 'primary' \\| 'secondary'` | No | `'default'` |\n| **topText** <Tippy>Text displayed above the image</Tippy> | `string` | No | - |\n| **topTextComponent** <Tippy>Custom component for top text</Tippy> | `React.ReactNode` | No | - |\n| **imageSrc** <Tippy>URL of the image to display</Tippy> | `string` | No | - |\n| **imageAlt** <Tippy>Alt text for the image</Tippy> | `string` | No | `''` |\n| **imageComponent** <Tippy>Custom component instead of default Image</Tippy> | `React.ReactNode` | No | - |\n| **imageFill** <Tippy>Whether the image should fill its container</Tippy> | `boolean` | No | `true` |\n| **imageHeight** <Tippy>Height of the image in pixels</Tippy> | `number` | No | `100` |\n| **imageWidth** <Tippy>Width of the image in pixels</Tippy> | `number` | No | `100` |\n| **bottomText** <Tippy>Text displayed below the image</Tippy> | `string` | No | - |\n| **bottomTextComponent** <Tippy>Custom component for bottom text</Tippy> | `React.ReactNode` | No | - |\n| **colSpan** <Tippy>Number of columns the item should span</Tippy> | `1 \\| 2 \\| 3 \\| 4` | No | `1` |\n| **rowSpan** <Tippy>Number of rows the item should span</Tippy> | `1 \\| 2 \\| 3` | No | `1` |\n| **className** <Tippy>Additional CSS classes</Tippy> | `string` | No | `''` |\n| **href** <Tippy>Link URL to make the item clickable</Tippy> | `string` | No | - |\n| **asChild** <Tippy>Whether to render children as the main component</Tippy> | `boolean` | No | `false` |\n</PropsReference>"
            },
            "BlogList": {
              "description": "The `LandingBlogList` component displays a collection of blog posts (`LandingBlogPost`) in either a grid or list format. It includes customizable titles, descriptions, and various styling options.",
              "api": "<Usage>\nImport the component and use it to display a collection of blog posts:\n\n```jsx\nimport { LandingBlogList } from '@/components/landing/LandingBlogList';\nimport { LandingBlogPost } from '@/components/landing/blog/LandingBlogPost';\n```\n\n```jsx\n<LandingBlogList\n  title=\"Latest Articles\"\n  description=\"Check out our latest blog posts and stay updated with the newest trends.\"\n>\n  <LandingBlogPost\n    post={{\n      slug: \"getting-started\",\n      date: \"2023-08-10\",\n      title: \"Getting Started with Shipixen\",\n      summary: \"Learn how to set up your first project using Shipixen templates and components.\",\n      tags: [\"Tutorial\", \"Beginners\"],\n      images: [\"/static/images/backdrop-1.webp\"],\n      readingTime: 5,\n      author: {\n        name: \"Jack Doe\",\n        avatar: \"/static/images/people/1.webp\"\n      }\n    }}\n  />\n</LandingBlogList>\n```\n</Usage>\n\n<Examples>\n### Grid Display\n\nDisplay blog posts in a grid format instead of a list.\n\n\n\n```jsx\n<LandingBlogList\n  title=\"Featured Articles\"\n  description=\"Our most popular content curated for you.\"\n  display=\"grid\"\n>\n  <LandingBlogPost\n    post={{\n      slug: \"react-patterns\",\n      date: \"2023-07-15\",\n      title: \"Essential React Patterns\",\n      summary: \"Learn the essential patterns that every React developer should know.\",\n      tags: [\"React\", \"Patterns\"],\n      images: [\"/static/images/backdrop-3.webp\"],\n      readingTime: 6,\n      author: {\n        name: \"Alex Johnson\",\n        avatar: \"/static/images/people/3.webp\"\n      }\n    }}\n  />\n  <LandingBlogPost\n    post={{\n      slug: \"tailwind-tips\",\n      date: \"2023-07-10\",\n      title: \"Tailwind CSS Pro Tips\",\n      summary: \"Improve your Tailwind workflow with these professional tips and tricks.\",\n      tags: [\"CSS\", \"Tailwind\"],\n      images: [\"/static/images/backdrop-4.webp\"],\n      readingTime: 4,\n      author: {\n        name: \"Brett Carlsen\",\n        avatar: \"/static/images/people/4.webp\"\n      }\n    }}\n  />\n</LandingBlogList>\n```\n\n\n\n### With Background and Left-Aligned Text\n\nAdd a background and left-align the title and description.\n\n\n\n```jsx\n<LandingBlogList\n  title=\"Latest Updates\"\n  description=\"Stay updated with our newest content and announcements.\"\n  textPosition=\"left\"\n  withBackground={true}\n>\n  <LandingBlogPost\n    post={{\n      slug: \"nextjs-13\",\n      date: \"2023-08-01\",\n      title: \"Next.js 13 Features You Should Know\",\n      summary: \"Explore the most important features and improvements in Next.js 13.\",\n      tags: [\"Next.js\", \"Features\"],\n      images: [\"/static/images/backdrop-5.webp\"],\n      readingTime: 7,\n      author: {\n        name: \"Michael Brown\",\n        avatar: \"/static/images/people/5.webp\"\n      }\n    }}\n  />\n</LandingBlogList>\n```\n\n\n\n### Secondary Variant with Background Glow\n\nUse the secondary color variant with a background glow effect.\n\n\n\n```jsx\n<LandingBlogList\n  title=\"Editor's Picks\"\n  description=\"Hand-picked articles from our editorial team.\"\n  variant=\"secondary\"\n  withBackgroundGlow={true}\n>\n  <LandingBlogPost\n    post={{\n      slug: \"typescript-tips\",\n      date: \"2023-07-25\",\n      title: \"TypeScript Tips for React Developers\",\n      summary: \"Improve your React applications with these TypeScript best practices.\",\n      tags: [\"TypeScript\", \"React\"],\n      images: [\"/static/images/backdrop-6.webp\"],\n      readingTime: 6,\n      author: {\n        name: \"Timothy Billow\",\n        avatar: \"/static/images/people/6.webp\"\n      }\n    }}\n  />\n</LandingBlogList>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **children** <Tippy>The blog post components to render inside the list</Tippy> | `ReactNode` | Yes | - |\n| **title** <Tippy>The main heading for the blog list section</Tippy> | `string` | No | - |\n| **titleComponent** <Tippy>Custom component to use instead of the default title</Tippy> | `ReactNode` | No | - |\n| **description** <Tippy>The description text displayed below the title</Tippy> | `string` | No | - |\n| **descriptionComponent** <Tippy>Custom component to use instead of the default description</Tippy> | `ReactNode` | No | - |\n| **className** <Tippy>Additional CSS classes to apply to the section</Tippy> | `string` | No | - |\n| **variant** <Tippy>Color theme variant to use for styling</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **withBackground** <Tippy>Whether to show a colored background</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Whether to show a gradient background glow effect</Tippy> | `boolean` | No | `false` |\n| **backgroundGlowVariant** <Tippy>Color variant for the background glow</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **textPosition** <Tippy>Alignment of the title and description</Tippy> | `'center' \\| 'left'` | No | `'center'` |\n| **display** <Tippy>How to display the blog posts - as a grid or list</Tippy> | `'grid' \\| 'list'` | No | `'list'` |\n\n```ts\nexport interface BlogPost {\n  path: string; // URL path of the blog post\n  date: string; // Publication date of the post\n  title: string; // Title of the blog post\n  summary?: string; // Brief summary of the blog post content\n  tags?: string[] | Array<{url: string; text: string}>; // Array of category tags (strings) or objects with url and text properties\n  images?: string[]; // Array of image URLs, with the first one used as the post thumbnail\n  readingTime?: number; // Estimated reading time in minutes\n  author?: {\n    name?: string; // Name of the blog post author\n    avatar?: string; // URL to the author's avatar image\n  };\n}\n```\n</PropsReference>"
            },
            "BlogPost": {
              "description": "The `LandingBlogPost` component displays a single blog post card with an image, author information, date, title, summary, reading time, and tags. It's designed to be used within a blog list component but can also function as a standalone element. Can be used in a `LandingBlogList` component or as a standalone element.",
              "api": "<Usage>\nImport the component and provide a post object with the required properties:\n\n```jsx\nimport { LandingBlogPost } from '@/components/landing/blog/LandingBlogPost';\n```\n\n```jsx\n<LandingBlogPost\n  post={{\n    slug: 'blog-post-slug',\n    date: 'November 15, 2023',\n    title: 'Blog Post Title',\n    summary: 'This is a summary of the blog post content.',\n    tags: ['Tag 1', 'Tag 2'],\n    images: ['/static/images/backdrop-1.webp'],\n    readingTime: 5,\n    author: {\n      name: 'John Doe',\n      avatar: '/static/images/people/1.webp'\n    }\n  }}\n/>\n```\n</Usage>\n\n<Examples>\n### Basic Blog Post\n\nA minimal blog post card with only the essential information:\n\n\n\n```jsx\nimport { LandingBlogPost } from '@/components/landing/blog/LandingBlogPost';\n\n<LandingBlogPost\n  post={{\n    slug: 'minimal-post',\n    date: 'November 10, 2023',\n    title: 'Minimal Blog Post Example',\n    summary: 'This is a minimal blog post example with only the essential information.',\n  }}\n/>\n```\n\n\n\n### Image Position Variants\n\nThe `LandingBlogPost` component supports three image position layouts:\n\n#### Left Image Layout\n\n\n\n```jsx\nimport { LandingBlogPost } from '@/components/landing/blog/LandingBlogPost';\n\n<LandingBlogPost\n  post={{\n    slug: 'left-image-post',\n    date: 'November 12, 2023',\n    title: 'Blog Post with Left Image',\n    summary: 'This example shows a blog post with the image positioned on the left side.',\n    images: ['/static/images/backdrop-5.webp'],\n    readingTime: 4\n  }}\n  imagePosition=\"left\"\n/>\n```\n\n\n\n#### Center Image Layout\n\nUsed by default in grid displays:\n\n\n\n```jsx\nimport { LandingBlogPost } from '@/components/landing/blog/LandingBlogPost';\n\n<LandingBlogPost\n  post={{\n    slug: 'center-image-post',\n    date: 'November 13, 2023',\n    title: 'Blog Post with Center Image',\n    summary: 'This example shows a blog post with the image positioned at the top (center layout).',\n    images: ['/static/images/backdrop-9.webp'],\n    readingTime: 5\n  }}\n  imagePosition=\"center\"\n/>\n```\n\n\n\n#### Right Image Layout (Default)\n\nUsed by default in list displays:\n\n\n\n```jsx\nimport { LandingBlogPost } from '@/components/landing/blog/LandingBlogPost';\n\n<LandingBlogPost\n  post={{\n    slug: 'right-image-post',\n    date: 'November 14, 2023',\n    title: 'Blog Post with Right Image',\n    summary: 'This example shows a blog post with the image positioned on the right side.',\n    images: ['/static/images/backdrop-7.webp'],\n    readingTime: 6\n  }}\n  imagePosition=\"right\" // This is the default, so it can be omitted\n/>\n```\n\n\n\n### With Image and Summary\n\nA blog post card featuring an image and summary text:\n\n\n\n```jsx\nimport { LandingBlogPost } from '@/components/landing/blog/LandingBlogPost';\n\n<LandingBlogPost\n  post={{\n    slug: 'image-post',\n    date: 'November 12, 2023',\n    title: 'Blog Post with Image and Summary',\n    summary: 'This is a brief summary of the blog post that gives readers an idea of what to expect.',\n    images: ['/static/images/backdrop-5.webp']\n  }}\n/>\n```\n\n\n\n### With Author and Reading Time\n\nA blog post with author information and reading time displayed:\n\n\n\n```jsx\nimport { LandingBlogPost } from '@/components/landing/blog/LandingBlogPost';\n\n<LandingBlogPost\n  post={{\n    slug: 'author-post',\n    date: 'November 14, 2023',\n    title: 'Blog Post with Author Information',\n    summary: 'A comprehensive guide written by an expert in the field.',\n    readingTime: 8,\n    author: {\n      name: 'Sarah Williams',\n      avatar: '/static/images/people/3.webp'\n    }\n  }}\n/>\n```\n\n\n\n### With Tags\n\nA blog post card displaying category tags:\n\n\n\n```jsx\nimport { LandingBlogPost } from '@/components/landing/blog/LandingBlogPost';\n\n<LandingBlogPost\n  post={{\n    slug: 'tagged-post',\n    date: 'November 16, 2023',\n    title: 'Blog Post with Category Tags',\n    summary: 'Learn about the latest features and updates in our newest release.',\n    tags: ['New Features', 'Updates', 'Tutorial'],\n    readingTime: 6\n  }}\n/>\n```\n\n\n\n### With Clickable Tags\n\nA blog post with tags that link to other pages:\n\n\n\n```jsx\nimport { LandingBlogPost } from '@/components/landing/blog/LandingBlogPost';\n\n<LandingBlogPost\n  post={{\n    slug: 'clickable-tags-post',\n    date: 'November 20, 2023',\n    title: 'Blog Post with Clickable Tags',\n    summary: 'This example shows a blog post with tags that link to other pages when clicked.',\n    tags: [\n      { url: '/tags/react', text: 'React' },\n      { url: '/tags/nextjs', text: 'Next.js' }\n    ],\n    readingTime: 7\n  }}\n/>\n```\n\n\n\n### Complete Example\n\nA fully-featured blog post card with all available properties:\n\n\n\n```jsx\nimport { LandingBlogPost } from '@/components/landing/blog/LandingBlogPost';\n\n<LandingBlogPost\n  post={{\n    path: '/blog/complete-example',\n    slug: 'complete-example',\n    date: 'November 18, 2023',\n    title: 'The Complete Guide to Modern Web Development',\n    summary: 'Explore the latest tools, frameworks, and best practices for building exceptional web experiences in 2023 and beyond.',\n    tags: ['Web Development', 'Tutorial', 'Best Practices'],\n    images: ['/static/images/backdrop-10.webp'],\n    readingTime: 12,\n    author: {\n      name: 'Michael Chen',\n      avatar: '/static/images/people/4.webp'\n    }\n  }}\n/>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **post** <Tippy>Blog post object with post details</Tippy> | `BlogPost` | Yes | - |\n| **imagePosition** <Tippy>Controls the layout of the post image</Tippy> | `'left' \\| 'center' \\| 'right'` | No | `'right'` |\n\n\n```ts\nexport interface BlogPost {\n  slug: string; // Unique identifier for the blog post\n  date: string; // Publication date of the post\n  title: string; // Title of the blog post\n  summary?: string; // Brief summary of the blog post content\n  tags?: string[] | Array<{url: string; text: string}>; // Array of category tags (strings) or objects with url and text properties\n  images?: string[]; // Array of image URLs, with the first one used as the post thumbnail\n  readingTime?: number; // Estimated reading time in minutes\n  author?: {\n    name?: string; // Name of the blog post author\n    avatar?: string; // URL to the author's avatar image\n  };\n}\n```\n</PropsReference>"
            },
            "Discount": {
              "description": "Use this component to show a discount or offer to encourage users to take action, typically used under call to action buttons.",
              "api": "<Usage>\n```jsx\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\n```\n\n```jsx\n<LandingDiscount\n  discountValueText=\"$350 off\"\n  discountDescriptionText=\"for the first 10 customers (2 left)\"\n/>\n```\n</Usage>\n\n<Examples>\n### No animation\n\n\n  ```jsx\n  <LandingDiscount\n    animated={false}\n    discountValueText=\"$99 off\"\n    discountDescriptionText=\"for a limited time\"\n  />\n  ```\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                        | Prop Type | Required | Default                                 |\n| -------------------------------------------------------------------------------- | --------- | -------- | --------------------------------------- |\n| **discountValueText** <Tippy>Text to display the value of the discount</Tippy>   | `string`  | Yes      | `'$200 off'`                            |\n| **discountDescriptionText** <Tippy>Text to describe the discount details</Tippy> | `string`  | Yes      | `'for the first 50 customers (5 left)'` |\n| **animated** <Tippy>Controls whether the icon should have an animation</Tippy>   | `boolean` | No       | `true`                                  |\n</PropsReference>"
            },
            "FaqCollapsible": {
              "description": "This component displays a collapsible list of frequently asked questions and their answers.",
              "api": "<Usage>\n```jsx\nimport { LandingFaqCollapsibleSection } from '@/components/landing/LandingFaqCollapsible';\n```\n\n```jsx\nconst faqItems = [\n  {\n    question: 'Can I get a refund?',\n    answer:\n      'Yes, you can get a refund within 30 days of your purchase. No questions asked.',\n  },\n  {\n    question: 'What technologies are used?',\n    answer: 'We use Next.js, Tailwind CSS, and Vercel for the deployment.',\n  },\n  {\n    question: 'What do I get if I pre-order?',\n    answer:\n      'With the pre-order, you get a 50% discount on the final price and a lifetime license for the generated code.',\n  },\n];\n```\n\n```jsx\n<LandingFaqCollapsibleSection\n  title=\"FAQ\"\n  description=\"Looking to learn more about our product? Here are some of the most common\n  questions.\"\n  faqItems={faqItems}\n/>\n```\n</Usage>\n\n<Examples>\n### With Background\n\n\n\n```jsx\nimport { LandingFaqCollapsibleSection } from '@/components/landing/LandingFaqCollapsible';\n\nconst faqItems = [\n  {\n    question: 'Can I get a refund?',\n    answer:\n      'Yes, you can get a refund within 30 days of your purchase. No questions asked.',\n  },\n  {\n    question: 'What technologies are used?',\n    answer:\n      'We use Next.js, Tailwind CSS, and Vercel for the deployment.',\n  },\n  {\n    question: 'What do I get if I pre-order?',\n    answer:\n      'With the pre-order, you get a 50% discount on the final price and a lifetime license for the generated code.',\n  },\n]\n\n<LandingFaqCollapsibleSection\n  withBackground\n  variant=\"secondary\"\n  title='FAQ'\n  description=\"Looking to learn more about our product? Here are some of the most common\n  questions.\"\n  faqItems={faqItems}\n/>\n```\n\n\n\n### With Background Glow\n\n\n\n```jsx\nimport { LandingFaqCollapsibleSection } from '@/components/landing/LandingFaqCollapsible';\n\nconst faqItems = [\n  {\n    question: 'Can I get a refund?',\n    answer:\n      'Yes, you can get a refund within 30 days of your purchase. No questions asked.',\n  },\n  {\n    question: 'What technologies are used?',\n    answer:\n      'We use Next.js, Tailwind CSS, and Vercel for the deployment.',\n  },\n  {\n    question: 'What do I get if I pre-order?',\n    answer:\n      'With the pre-order, you get a 50% discount on the final price and a lifetime license for the generated code.',\n  },\n]\n\n<LandingFaqCollapsibleSection\n  withBackgroundGlow\n  backgroundGlowVariant=\"secondary\"\n  title='FAQ'\n  description=\"Looking to learn more about our product? Here are some of the most common\n  questions.\"\n  faqItems={faqItems}\n/>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                      | Prop Type                    | Required | Default     |\n| ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------- | -------- | ----------- |\n| **title** <Tippy>A title to be displayed above the FAQ section.</Tippy>                                                        | `string` ǀ `React.ReactNode` | No       | -           |\n| **titleComponent** <Tippy>A custom component to be used as the title instead of a string.</Tippy>                              | `React.ReactNode`            | No       | -           |\n| **description** <Tippy>A description to be displayed below the title.</Tippy>                                                  | `string` ǀ `React.ReactNode` | No       | -           |\n| **descriptionComponent** <Tippy>A custom component to be used as the description instead of a string.</Tippy>                  | `React.ReactNode`            | No       | -           |\n| **faqItems** <Tippy>An array of FAQ items containing question and answer strings.</Tippy>                                      | `FaqItem[]`                  | Yes      | -           |\n| **withBackground** <Tippy>Determines whether to display a background or not.</Tippy>                                           | `boolean`                    | No       | `false`     |\n| **withBackgroundGlow** <Tippy>Determines whether to display a glowing background effect or not.</Tippy>                        | `boolean`                    | No       | `false`     |\n| **variant** <Tippy>Determines the color variant of the section (primary or secondary).</Tippy>                                 | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n| **backgroundGlowVariant** <Tippy>Determines the color variant of the glowing background effect (primary or secondary).</Tippy> | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n\n```ts\nexport interface FaqItem {\n  question: string;\n  answer: string;\n}\n```\n</PropsReference>"
            },
            "Faq": {
              "description": "Use this component to display frequently asked questions & answers on the landing page. This can also be added on a separate page, but it's usually a good idea to have it on the landing page so people can search for information. <br/> Moreover, it can give you a bit of an SEO boost too.",
              "api": "<Usage>\n```jsx\nimport { LandingFaqSection } from '@/components/landing/LandingFaq';\n```\n\n```jsx\n<LandingFaqSection\n  title={'Frequently Asked Questions'}\n  description=\"Looking to learn more about Shipixen? Here are some of the most common\n  questions we get asked.\"\n  faqItems={[\n    {\n      question: 'What is Shipixen exactly?',\n      answer:\n        'Shipixen is an app that generates boilerplate code with your branding. You get the git repository & can modify the code as you want.',\n    },\n  ]}\n/>\n```\n</Usage>\n\n<Examples>\n### With Background\n\n\n\n```jsx\nimport { LandingFaqSection } from '@/components/landing/LandingFaq';\n\nconst faqItems = [\n  {\n    question: 'Can I get a refund?',\n    answer:\n      'Yes, you can get a refund within 30 days of your purchase. No questions asked.',\n  },\n  {\n    question: 'What technologies are used?',\n    answer:\n      'We use Next.js, Tailwind CSS, and Vercel for the deployment.',\n  },\n  {\n    question: 'What do I get if I pre-order?',\n    answer:\n      'With the pre-order, you get a 50% discount on the final price and a lifetime license for the generated code.',\n  },\n]\n\n<LandingFaqSection\n  withBackground\n  variant=\"secondary\"\n  title='FAQ'\n  description=\"Looking to learn more about our product? Here are some of the most common\n  questions.\"\n  faqItems={faqItems}\n/>\n```\n\n\n\n### With Background Glow\n\n\n\n```jsx\nimport { LandingFaqSection } from '@/components/landing/LandingFaq';\n\nconst faqItems = [\n  {\n    question: 'Can I get a refund?',\n    answer:\n      'Yes, you can get a refund within 30 days of your purchase. No questions asked.',\n  },\n  {\n    question: 'What technologies are used?',\n    answer:\n      'We use Next.js, Tailwind CSS, and Vercel for the deployment.',\n  },\n  {\n    question: 'What do I get if I pre-order?',\n    answer:\n      'With the pre-order, you get a 50% discount on the final price and a lifetime license for the generated code.',\n  },\n]\n\n<LandingFaqSection\n  withBackgroundGlow\n  backgroundGlowVariant=\"secondary\"\n  title='FAQ'\n  description=\"Looking to learn more about our product? Here are some of the most common\n  questions.\"\n  faqItems={faqItems}\n/>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                   | Prop Type                    | Required | Default     |\n| --------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -------- | ----------- |\n| **title** <Tippy>A string or React node representing the title of the FAQ section.</Tippy>                                  | `string` ǀ `React.ReactNode` | No       | -           |\n| **titleComponent** <Tippy>A React node representing the title of the FAQ section.</Tippy>                                   | `React.ReactNode`            | No       | -           |\n| **description** <Tippy>A string or React node representing the description of the FAQ section.</Tippy>                      | `string` ǀ `React.ReactNode` | No       | -           |\n| **descriptionComponent** <Tippy>A React node representing the description of the FAQ section.</Tippy>                       | `React.ReactNode`            | No       | -           |\n| **faqItems** <Tippy>An array of objects containing question and answer strings for the FAQ items.</Tippy>                   | `FaqItem[]`                  | Yes      | -           |\n| **withBackground** <Tippy>A boolean indicating whether the FAQ section should have a background.</Tippy>                    | `boolean`                    | No       | `false`     |\n| **withBackgroundGlow** <Tippy>A boolean indicating whether the FAQ section should have a glowing background effect.</Tippy> | `boolean`                    | No       | `false`     |\n| **variant** <Tippy>The variant of the background color, either `'primary'` or `'secondary'`.</Tippy>                        | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n| **backgroundGlowVariant** <Tippy>The variant of the glowing background, either `'primary'` or `'secondary'`.</Tippy>        | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n\n```ts\nexport interface FaqItem {\n  question: string;\n  answer: string;\n}\n```\n</PropsReference>"
            },
            "FeatureKeyPoints": {
              "description": "Use this to display a bullet point in the description of a [Product Feature](/boilerplate-documentation/landing-page-components/product-feature). This component can clarify the key features of the product or service, especially when the description is long and detailed. Can also be used as a standalone component.",
              "api": "<Usage>\n```jsx\nimport { LandingProductFeatureKeyPoints } from '@/components/landing/LandingProductFeatureKeyPoints';\n```\n\n```jsx\n<LandingProductFeatureKeyPoints\n  keyPoints={[\n    {\n      title: 'Intelligent Assistance',\n      description:\n        'Receive personalized recommendations and insights tailored to your workflow.',\n    },\n    {\n      title: 'Seamless Collaboration',\n      description:\n        'Easily collaborate with team members and clients in real-time.',\n    },\n    {\n      title: 'Advanced Customization',\n      description:\n        'Tailor your app to fit your unique requirements with extensive customization.',\n    },\n  ]}\n/>\n```\n</Usage>\n\n<Examples>\n### With Cta Section\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingProductFeatureKeyPoints } from '@/components/landing/LandingProductFeatureKeyPoints';\n\n<LandingPrimaryImageCtaSection\n  title=\"Want more?\"\n  descriptionComponent={\n    <LandingProductFeatureKeyPoints\n      keyPoints={[\n        {\n          title: 'Intelligent Assistance',\n          description:\n            'Receive personalized recommendations and insights tailored to your workflow.',\n        },\n        {\n          title: 'Seamless Collaboration',\n          description:\n            'Easily collaborate with team members and clients in real-time.',\n        },\n        {\n          title: 'Advanced Customization',\n          description:\n            'Tailor your app to fit your unique requirements with extensive customization.',\n        },\n      ]}\n    />\n  }\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n</LandingPrimaryImageCtaSection>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                               | Prop Type                   | Required | Default     |\n| ----------------------------------------------------------------------------------------------------------------------- | --------------------------- | -------- | ----------- |\n| **keyPoints** <Tippy>Array of objects each containing a title and description to be displayed as bullet points.</Tippy> | `KeyPoint[]`                | Yes      | -           |\n| **variant** <Tippy>Styling variant for the bullet points, determining the icon and text color.</Tippy>                  | `'primary'` ǀ `'secondary'` | No       | `'primary'` |\n| **icon** <Tippy>Custom icon to be displayed instead of the default checkmark.</Tippy>                                  | `React.ReactNode` ǀ `LucideIcon`          | No       | -           |\n| **descriptionStyle** <Tippy>Determines if the description should be displayed inline or as a block.</Tippy>             | `'inline'` ǀ `'block'`                  | No       | `'block'`   |\n| **iconClassName** <Tippy>Custom class name for the icon, use to override color and size etc.</Tippy>                                                      | `string`                        | No       | -           |\n\n```ts\nexport interface KeyPoint {\n  title: string;\n  description: string;\n}\n```\n</PropsReference>"
            },
            "FeatureList": {
              "description": "Use this component to showcase a list of features on the landing page. Each feature has a title, description and icon. Under the hood, this component uses the [Feature](/boilerplate-documentation/landing-page-components/feature) component.",
              "api": "<Usage>\n```jsx\nimport { LandingFeatureList } from '@/components/landing/feature/LandingFeatureList';\nimport { SparklesIcon } from 'lucide-react';\n```\n\n```\n<LandingFeatureList\n  title={\"Nothing quite like it.\"}\n  description={\n    'Shipixen sets up everything you need to start working on your blog, website or product.'\n  }\n  featureItems={[\n    {\n      title: 'Automatic deployment to Vercel',\n      description:\n        'Deploying the generated template to Vercel is as easy as clicking a button. ',\n      icon: <SparklesIcon />,\n    },\n  ]}\n/>\n```\n</Usage>\n\n<Examples>\n### With background\n\n\n\n```jsx\nimport { SparklesIcon, LineChartIcon, LayersIcon } from 'lucide-react';\nimport { LandingFeatureList } from '@/components/landing/feature/LandingFeatureList';\n\nconst featureItems = [\n  {\n    title: 'Deploy now',\n    description: 'Deploying to Vercel with a click.',\n    icon: <SparklesIcon />,\n  },\n  {\n    title: 'SEO optimized',\n    description: 'Shipixen is optimized for search engines.',\n    icon: <LineChartIcon />,\n  },\n  {\n    title: 'MDX blog ready',\n    description: 'Shipixen comes with a fully featured MDX blog. ',\n    icon: <LayersIcon />,\n  },\n]\n\n<LandingFeatureList\n  withBackground\n  variant=\"secondary\"\n  title={\"Nothing quite like it.\"}\n  description={\n    'Shipixen sets up everything you need to start working on your blog, website or product.'\n  }\n  featureItems={featureItems}\n/>\n```\n\n\n\n### With Background Glow\n\n\n\n```jsx\nimport { SparklesIcon, LineChartIcon, LayersIcon } from 'lucide-react';\nimport { LandingFeatureList } from '@/components/landing/feature/LandingFeatureList';\n\nconst featureItems = [\n  {\n    title: 'Automatic deployment to Vercel',\n    description:\n      'Deploying the generated template to Vercel is as easy as clicking a button. ',\n    icon: <SparklesIcon />,\n  },\n  {\n    title: 'Dynamic Social Image',\n    description:\n      'We generate an open graph image that will be visible when you share your site online.',\n    icon: <LineChartIcon />,\n  },\n  {\n    title: 'MDX blog, no server required',\n    description:\n      'Shipixen comes with a fully featured MDX blog. ',\n    icon: <LayersIcon />,\n  },\n]\n\n<LandingFeatureList\n  withBackgroundGlow\n  backgroundGlowVariant=\"primary\"\n  variant=\"secondary\"\n  title={\"Nothing quite like it.\"}\n  description={\n    'Shipixen sets up everything you need to start working on your blog, website or product.'\n  }\n  featureItems={featureItems}\n/>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                                     | Prop Type                    | Required | Default     |\n| --------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -------- | ----------- |\n| **title** <Tippy>A title for the feature list section.</Tippy>                                                                                | `string` ǀ `React.ReactNode` | No       | -           |\n| **titleComponent** <Tippy>A custom React component to be used instead of a string for the title.</Tippy>                                      | `React.ReactNode`            | No       | -           |\n| **description** <Tippy>A description for the feature list section.</Tippy>                                                                    | `string` ǀ `React.ReactNode` | No       | -           |\n| **descriptionComponent** <Tippy>A custom React component to be used instead of a string for the description.</Tippy>                          | `React.ReactNode`            | No       | -           |\n| **featureItems** <Tippy>An array of objects representing feature items. Each feature item should have a title, description, and icon.</Tippy> | `FeatureListItem[]`          | Yes      | -           |\n| **withBackground** <Tippy>Determines if the feature list section should have a background.</Tippy>                                            | `boolean`                    | No       | `false`     |\n| **withBackgroundGlow** <Tippy>Determines if the feature list section should have a glowing background effect.</Tippy>                         | `boolean`                    | No       | `false`     |\n| **variant** <Tippy>Determines the variant of the feature list section (primary or secondary).</Tippy>                                         | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n| **backgroundGlowVariant** <Tippy>Determines the variant of the glowing background effect (primary or secondary).</Tippy>                      | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n\n```ts\nexport interface FeatureListItem {\n  title: string;\n  description: string;\n  icon: React.ReactNode;\n}\n```\n</PropsReference>"
            },
            "Feature": {
              "description": "Use this component to display a single product feature. It has a title, description and icon. It should be used with the [FeatureList](/boilerplate-documentation/landing-page-components/feature-list) component, but can also be used standalone.",
              "api": "<Usage>\n```jsx\nimport { LandingFeature } from '@/components/landing/feature/LandingFeature';\nimport { SparklesIcon } from 'lucide-react';\n```\n\n```\n<LandingFeature\n  title='Automatic deployment to Vercel'\n  description='Deploying the generated template to Vercel is as easy as clicking a button. There is no need to configure anything.'\n  icon={<SparklesIcon />}\n/>\n```\n</Usage>\n\n<PropsReference>\n| Prop Name                                                                               | Prop Type                 | Required | Default     |\n| --------------------------------------------------------------------------------------- | ------------------------- | -------- | ----------- |\n| **title** <Tippy>This is the title of the feature.</Tippy>                              | `string`                  | Yes      | -           |\n| **titleComponent** <Tippy>This is the title of the feature.</Tippy>                  | `React.ReactNode`                  | No      | -           |\n| **description** <Tippy>This is the description of the feature.</Tippy>                  | `string`                  | Yes      | -           |\n| **descriptionComponent** <Tippy>This is the description of the feature.</Tippy>                  | `React.ReactNode`                  | No      | -           |\n| **icon** <Tippy>The icon representing the feature.</Tippy>                              | `React.ReactNode`         | Yes      | -           |\n| **variant** <Tippy>The variant of the feature, either 'primary' or 'secondary'.</Tippy> | `'primary' ǀ 'secondary'` | No       | `'primary'` |\n</PropsReference>"
            },
            "Footer": {
              "description": "This component displays a footer on the landing page. It provides additional information and links related to the website. On smaller screens, it changes to a horizontal layout to ensure usability.",
              "api": "<Usage>\n```jsx\nimport { LandingFooter } from '@/components/landing/footer/LandingFooter';\nimport { LandingFooterColumn } from '@/components/landing/footer/LandingFooterColumn';\nimport { LandingFooterLink } from '@/components/landing/footer/LandingFooterLink';\n```\n\n```jsx\n<LandingFooter\n  title=\"Beautiful landing pages in minutes\"\n  description=\"The easiest way to make a landing page for your startup: SaaS, iOS/Android app, directory, personal page and more.\"\n  footnote=\"© 2025 Page AI. All rights reserved.\"\n>\n  <LandingFooterColumn title=\"Product\">\n    <LandingFooterLink href=\"#\">Features</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Pricing</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Integrations</LandingFooterLink>\n    <LandingFooterLink href=\"#\">FAQ</LandingFooterLink>\n  </LandingFooterColumn>\n\n  <LandingFooterColumn title=\"Company\">\n    <LandingFooterLink href=\"#\">About</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Careers</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Press</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Blog</LandingFooterLink>\n  </LandingFooterColumn>\n</LandingFooter>\n```\n</Usage>\n\n<Examples>\n### With custom logo\n\n\n```jsx\nimport { LandingFooter } from '@/components/landing/footer/LandingFooter';\nimport { LandingFooterColumn } from '@/components/landing/footer/LandingFooterColumn';\nimport { LandingFooterLink } from '@/components/landing/footer/LandingFooterLink';\n\n<LandingFooter\n  title=\"Beautiful landing pages in minutes\"\n  description=\"The easiest way to make a landing page for your startup: SaaS, iOS/Android app, directory, personal page and more.\"\n  footnote=\"© 2025 Page AI. All rights reserved.\" logoComponent={<Image width={40} height={40} className=\"rounded-full\" src=\"https://picsum.photos/id/250/200/200\" />}\n>\n  <LandingFooterColumn title=\"Product\">\n    <LandingFooterLink href=\"#\">Features</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Pricing</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Integrations</LandingFooterLink>\n    <LandingFooterLink href=\"#\">FAQ</LandingFooterLink>\n  </LandingFooterColumn>\n\n  <LandingFooterColumn title=\"Company\">\n    <LandingFooterLink href=\"#\">About</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Careers</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Press</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Blog</LandingFooterLink>\n  </LandingFooterColumn>\n</LandingFooter>\n```\n\n\n\n### With more columns\n\n\n\n```jsx\nimport { LandingFooter } from '@/components/landing/footer/LandingFooter';\nimport { LandingFooterColumn } from '@/components/landing/footer/LandingFooterColumn';\nimport { LandingFooterLink } from '@/components/landing/footer/LandingFooterLink';\n\n<LandingFooter\n  title=\"Beautiful landing pages in minutes\"\n  description=\"The easiest way to make a landing page for your startup: SaaS, iOS/Android app, directory, personal page and more.\"\n  footnote=\"© 2025 Page AI. All rights reserved.\"\n>\n  <LandingFooterColumn title=\"Product\">\n    <LandingFooterLink href=\"#\">Features</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Pricing</LandingFooterLink>\n    <LandingFooterLink href=\"#\">API</LandingFooterLink>\n    <LandingFooterLink href=\"#\">FAQ</LandingFooterLink>\n  </LandingFooterColumn>\n\n  <LandingFooterColumn title=\"Help\">\n    <LandingFooterLink href=\"#\">Docs</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Support</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Contact</LandingFooterLink>\n  </LandingFooterColumn>\n\n  <LandingFooterColumn title=\"More\">\n    <LandingFooterLink href=\"#\">About</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Careers</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Press</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Blog</LandingFooterLink>\n  </LandingFooterColumn>\n</LandingFooter>\n```\n\n\n\n### With background\n\n\n```jsx\nimport { LandingFooter } from '@/components/landing/footer/LandingFooter';\nimport { LandingFooterColumn } from '@/components/landing/footer/LandingFooterColumn';\nimport { LandingFooterLink } from '@/components/landing/footer/LandingFooterLink';\n\n<LandingFooter\n  title=\"Beautiful landing pages in minutes\"\n  description=\"The easiest way to make a landing page for your startup: SaaS, iOS/Android app, directory, personal page and more.\"\n  footnote=\"© 2025 Page AI. All rights reserved.\"\n  withBackground\n>\n  <LandingFooterColumn title=\"Product\">\n    <LandingFooterLink href=\"#\">Features</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Pricing</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Integrations</LandingFooterLink>\n    <LandingFooterLink href=\"#\">FAQ</LandingFooterLink>\n  </LandingFooterColumn>\n\n  <LandingFooterColumn title=\"Company\">\n    <LandingFooterLink href=\"#\">About</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Careers</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Press</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Blog</LandingFooterLink>\n  </LandingFooterColumn>\n</LandingFooter>\n```\n\n\n\n### With background glow\n\n\n```jsx\nimport { LandingFooter } from '@/components/landing/footer/LandingFooter';\nimport { LandingFooterColumn } from '@/components/landing/footer/LandingFooterColumn';\nimport { LandingFooterLink } from '@/components/landing/footer/LandingFooterLink';\n\n<LandingFooter\n  title=\"Beautiful landing pages in minutes\"\n  description=\"The easiest way to make a landing page for your startup: SaaS, iOS/Android app, directory, personal page and more.\"\n  footnote=\"© 2025 Page AI. All rights reserved.\"\n  withBackgroundGlow\n>\n  <LandingFooterColumn title=\"Product\">\n    <LandingFooterLink href=\"#\">Features</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Pricing</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Integrations</LandingFooterLink>\n    <LandingFooterLink href=\"#\">FAQ</LandingFooterLink>\n  </LandingFooterColumn>\n\n  <LandingFooterColumn title=\"Company\">\n    <LandingFooterLink href=\"#\">About</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Careers</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Press</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Blog</LandingFooterLink>\n  </LandingFooterColumn>\n</LandingFooter>\n```\n\n\n\n### With background gradient\n\n\n```jsx\nimport { LandingFooter } from '@/components/landing/footer/LandingFooter';\nimport { LandingFooterColumn } from '@/components/landing/footer/LandingFooterColumn';\nimport { LandingFooterLink } from '@/components/landing/footer/LandingFooterLink';\n\n<LandingFooter\n  title=\"Beautiful landing pages in minutes\"\n  description=\"The easiest way to make a landing page for your startup: SaaS, iOS/Android app, directory, personal page and more.\"\n  footnote=\"© 2025 Page AI. All rights reserved.\"\n  withBackgroundGradient\n>\n  <LandingFooterColumn title=\"Product\">\n    <LandingFooterLink href=\"#\">Features</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Pricing</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Integrations</LandingFooterLink>\n    <LandingFooterLink href=\"#\">FAQ</LandingFooterLink>\n  </LandingFooterColumn>\n\n  <LandingFooterColumn title=\"Company\">\n    <LandingFooterLink href=\"#\">About</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Careers</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Press</LandingFooterLink>\n    <LandingFooterLink href=\"#\">Blog</LandingFooterLink>\n  </LandingFooterColumn>\n</LandingFooter>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name | Prop Type | Required | Default |\n| --------- | --------- | -------- | ------- |\n| **children** <Tippy>React nodes to be rendered within the footer.</Tippy> | `React.ReactNode` | No | - |\n| **title** <Tippy>The title text displayed in the footer.</Tippy> | `string` | No | - |\n| **description** <Tippy>A brief description displayed below the title.</Tippy> | `string` | No | - |\n| **footnote** <Tippy>Text or React nodes to be displayed as a footnote.</Tippy> | `string` ǀ `React.ReactNode` | No | - |\n| **logoComponent** <Tippy>A custom React node to replace the default logo.</Tippy> | `React.ReactNode` | No | - |\n| **withBackground** <Tippy>Determines if the footer should have a background color.</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Adds a glowing background effect to the footer.</Tippy> | `boolean` | No | `false` |\n| **variant** <Tippy>The color variant of the footer.</Tippy> | `'primary'` ǀ `'secondary'` | No | `'primary'` |\n| **backgroundGlowVariant** <Tippy>The color variant of the glowing background.</Tippy> | `'primary'` ǀ `'secondary'` | No | `'primary'` |\n| **withBackgroundGradient** <Tippy>Determines if the footer should have a gradient background.</Tippy> | `boolean` | No | `false` |\n</PropsReference>"
            },
            "LeadingPill": {
              "description": "The `LandingLeadingPill` is a versatile inline component perfect for announcements, badges, feature highlights, and call-to-action elements. It features **SVG-based borders** for all variants ensuring perfect rounded corners, supports multiple color variants including rainbow gradients, glass backgrounds, and can be made clickable as either a link or button. It also supports left and right components, custom text components, children, and custom opacity. **Key Features:** - **Rainbow gradient borders** with smooth color transitions - **Glass background effects** with backdrop blur - **Flexible content** support - **Clickable variants** (link or button) - **Responsive design** with proper dimension handling These work well together with `LandingPrimaryImageCtaSection`, `LandingPrimaryTextCtaSection`, `LandingPrimaryVideoCtaSection`, `LandingProductFeature`, `LandingProductVideoFeature` as the `leadingComponent` prop.",
              "api": "<Usage>\n```js\nimport { LandingLeadingPill } from '@/components/landing/leading/LandingLeadingPill';\n```\n\n```jsx\n<LandingLeadingPill\n  text=\"New Feature\"\n  borderVariant=\"primary\"\n  textVariant=\"primary\"\n/>\n```\n</Usage>\n\n<PropsReference>\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **className** <Tippy>Additional CSS classes to apply to the pill</Tippy> | `string` | No | `-` |\n| **textVariant** <Tippy>Text color variant of the pill</Tippy> | `'default' \\| 'primary' \\| 'secondary' \\| 'lightGray' \\| 'darkGray' \\| 'white' \\| 'black'` | No | `'default'` |\n| **borderVariant** <Tippy>Border style variant of the pill (all rendered with SVG)</Tippy> | `'default' \\| 'primary' \\| 'secondary' \\| 'lightGray' \\| 'darkGray' \\| 'pinkRainbow' \\| 'purpleRainbow' \\| 'yellowRainbow' \\| 'greenRainbow'` | No | `'default'` |\n| **backgroundVariant** <Tippy>Background style of the pill (only applied when withBackground is true)</Tippy> | `'default' \\| 'primary' \\| 'secondary' \\| 'glass' \\| 'primaryGlass' \\| 'secondaryGlass'` | No | `'default'` |\n| **withBackground** <Tippy>Whether to apply background styling</Tippy> | `boolean` | No | `false` |\n| **withBorder** <Tippy>Whether to apply border styling</Tippy> | `boolean` | No | `true` |\n| **borderWidth** <Tippy>Width of the border in pixels</Tippy> | `number` | No | `1` |\n| **text** <Tippy>Text content to display in the pill</Tippy> | `string` | No | `-` |\n| **textComponent** <Tippy>Custom React component to display instead of text</Tippy> | `React.ReactNode` | No | `-` |\n| **children** <Tippy>Children components to display (takes precedence over text and textComponent)</Tippy> | `React.ReactNode` | No | `-` |\n| **textStyle** <Tippy>Text styling variant</Tippy> | `'default' \\| 'capitalize' \\| 'uppercase'` | No | `'default'` |\n| **leftComponent** <Tippy>Component to display on the left side of the text</Tippy> | `React.ReactNode` | No | `-` |\n| **rightComponent** <Tippy>Component to display on the right side of the text</Tippy> | `React.ReactNode` | No | `-` |\n| **href** <Tippy>URL to navigate to when clicked (makes the pill a link)</Tippy> | `string` | No | `-` |\n| **onClick** <Tippy>Function to call when clicked (makes the pill a button)</Tippy> | `() => void` | No | `-` |\n</PropsReference>"
            },
            "Marquee": {
              "description": "This component displays an animated marquee that can loop through images, icons, or text.",
              "api": "<Usage>\n```jsx\nimport { LandingMarquee } from '@/components/landing/LandingMarquee';\n```\n\n```jsx\n<LandingMarquee>\n  <ChromeIcon className=\"w-12 h-12 mx-8\" />\n  <FigmaIcon className=\"w-12 h-12 mx-8\" />\n  <GithubIcon className=\"w-12 h-12 mx-8\" />\n  <FramerIcon className=\"w-12 h-12 mx-8\" />\n  <TwitchIcon className=\"w-12 h-12 mx-8\" />\n  <TwitterIcon className=\"w-12 h-12 mx-8\" />\n  <GitlabIcon className=\"w-12 h-12 mx-8\" />\n  <InstagramIcon className=\"w-12 h-12 mx-8\" />\n  <SlackIcon className=\"w-12 h-12 mx-8\" />\n</LandingMarquee>\n```\n</Usage>\n\n<Examples>\n### Animation Direction\n\n\n\n```jsx\nimport {\n  ChromeIcon,\n  FigmaIcon,\n  GithubIcon,\n  FramerIcon,\n  TwitchIcon,\n  TwitterIcon,\n  GitlabIcon,\n  InstagramIcon,\n  SlackIcon,\n} from 'lucide-react';\nimport { LandingMarquee } from '@/components/landing/LandingMarquee';\n\n<LandingMarquee animationDirection=\"left\">\n  <ChromeIcon className=\"w-12 h-12 mx-8\" />\n  <FigmaIcon className=\"w-12 h-12 mx-8\" />\n  <GithubIcon className=\"w-12 h-12 mx-8\" />\n  <FramerIcon className=\"w-12 h-12 mx-8\" />\n  <TwitchIcon className=\"w-12 h-12 mx-8\" />\n  <TwitterIcon className=\"w-12 h-12 mx-8\" />\n  <GitlabIcon className=\"w-12 h-12 mx-8\" />\n  <InstagramIcon className=\"w-12 h-12 mx-8\" />\n  <SlackIcon className=\"w-12 h-12 mx-8\" />\n</LandingMarquee>;\n```\n\n\n\n### With images\n\n\n\n```jsx\n<LandingMarquee>\n  <div className=\"flex gap-8 px-4\">\n    {[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Doe',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alice Doe',\n      },\n      {\n        imageSrc: '/static/images/people/4.webp',\n        name: 'Bob Doe',\n      },\n      {\n        imageSrc: '/static/images/people/5.webp',\n        name: 'Eve Doe',\n      },\n    ].map((person, index) => (\n      <LandingAvatar\n        size=\"large\"\n        key={index}\n        imageSrc={person.imageSrc}\n        name={person.name}\n      />\n    ))}\n  </div>\n</LandingMarquee>\n```\n\n\n\n### With background\n\n\n\n    ```jsx\n    import { ChromeIcon, FigmaIcon, GithubIcon, FramerIcon, TwitchIcon, TwitterIcon, GitlabIcon, InstagramIcon, SlackIcon } from 'lucide-react';\n    import { LandingMarquee } from '@/components/landing/LandingMarquee';\n\n    <LandingMarquee withBackground variant=\"secondary\" animationDurationInSeconds=\"10s\">\n      <ChromeIcon className=\"w-12 h-12 mx-8\" />\n      <FigmaIcon className=\"w-12 h-12 mx-8\" />\n      <GithubIcon className=\"w-12 h-12 mx-8\" />\n      <FramerIcon className=\"w-12 h-12 mx-8\" />\n      <TwitchIcon className=\"w-12 h-12 mx-8\" />\n      <TwitterIcon className=\"w-12 h-12 mx-8\" />\n      <GitlabIcon className=\"w-12 h-12 mx-8\" />\n      <InstagramIcon className=\"w-12 h-12 mx-8\" />\n      <SlackIcon className=\"w-12 h-12 mx-8\" />\n    </LandingMarquee>;\n    ```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name <Tippy>Prop Description</Tippy>                                                                                             | Prop Type                    | Required | Default     |\n| ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -------- | ----------- |\n| **children** <Tippy>React nodes to be rendered within the component (below the text on smaller screen, right side on larger).</Tippy> | `React.ReactNode` ǀ `string` | No       | -           |\n| **innerClassName** <Tippy>Additional class names to apply to the inner marquee container.</Tippy>                                     | `string`                     | No       | -           |\n| **withBackground** <Tippy>Flag to determine if a background should be applied.</Tippy>                                                | `boolean`                    | No       | `false`     |\n| **animationDurationInSeconds** <Tippy>Duration of the marquee animation in seconds e.g. \"10s\".</Tippy>                                | `string`                     | No       | -           |\n| **animationDirection** <Tippy>Direction of the marquee animation ('left' or 'right').</Tippy>                                         | `'left'` ǀ `'right'`         | No       | -           |\n| **variant** <Tippy>Variant of the marquee background ('primary' or 'secondary').</Tippy>                                              | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n</PropsReference>"
            },
            "Navigation": {
              "description": "This component displays a top navigation / header bar with a main menu and a logo. On smaller screens, the main menu is hidden and a menu is displayed instead. The menu will open a side sheet with the main menu items.",
              "api": "<Usage>\n```jsx\nimport { LandingHeader } from '@/components/landing/navigation/LandingHeader';\nimport { LandingHeaderMenuItem } from '@/components/landing/navigation/LandingHeaderMenuItem';\n```\n\n```jsx\n<LandingHeader>\n  <LandingHeaderMenuItem href=\"#\" label=\"Home\" />\n  <LandingHeaderMenuItem href=\"#\" label=\"Pricing\" />\n  <LandingHeaderMenuItem href=\"#\" label=\"Articles\" />\n</LandingHeader>\n```\n</Usage>\n\n<Examples>\n### With custom logo\n\n\n```jsx\nimport Image from '@/components/shared/Image';\nimport { LandingHeader } from '@/components/landing/navigation/LandingHeader';\nimport { LandingHeaderMenuItem } from '@/components/landing/navigation/LandingHeaderMenuItem';\n\n<LandingHeader\n  logoComponent={<Image width={40} height={40} className=\"rounded-full\" src=\"https://picsum.photos/id/250/200/200\" />}\n>\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Home\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Pricing\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Articles\"\n  />\n</LandingHeader>\n```\n\n\n\n### With background\n\nThis adds a background to the navigation bar (centered on larger screens).\n\n\n\n```jsx\nimport { LandingHeader } from '@/components/landing/navigation/LandingHeader';\nimport { LandingHeaderMenuItem } from '@/components/landing/navigation/LandingHeaderMenuItem';\n\n<LandingHeader withBackground>\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Home\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Pricing\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Articles\"\n  />\n</LandingHeader>\n```\n\n\n\n### With button\n\n\n```jsx\nimport { LandingHeader } from '@/components/landing/navigation/LandingHeader';\nimport { LandingHeaderMenuItem } from '@/components/landing/navigation/LandingHeaderMenuItem';\n\n<LandingHeader>\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Home\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Pricing\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Articles\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Sign In\"\n    type=\"button\"\n  />\n</LandingHeader>\n```\n\n\n\n### With icon button (or other elements)\n\n\n```jsx\nimport { SearchIcon } from 'lucide-react';\nimport { LandingHeader } from '@/components/landing/navigation/LandingHeader';\nimport { LandingHeaderMenuItem } from '@/components/landing/navigation/LandingHeaderMenuItem';\n\n<LandingHeader>\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Home\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Pricing\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Articles\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Sign In\"\n    type=\"button\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label={<SearchIcon />}\n    type=\"icon-button\"\n    variant=\"ghost\"\n  />\n</LandingHeader>\n```\n\n\n\n### Variant\n\n\n```jsx\nimport ThemeSwitch from '@/components/shared/ThemeSwitch';\nimport { SearchIcon } from 'lucide-react';\nimport { LandingHeader } from '@/components/landing/navigation/LandingHeader';\nimport { LandingHeaderMenuItem } from '@/components/landing/navigation/LandingHeaderMenuItem';\n\n<LandingHeader withBackground variant=\"secondary\">\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Home\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Pricing\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Articles\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label=\"Sign In\"\n    type=\"button\"\n  />\n\n  <LandingHeaderMenuItem\n    href=\"#\"\n    label={<SearchIcon />}\n    type=\"icon-button\"\n    variant=\"ghost\"\n  />\n\n  <ThemeSwitch />\n</LandingHeader>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name | Prop Type | Required | Default |\n| --------- | --------- | -------- | ------- |\n| **logoComponent** <Tippy>An optional React element to be used as the logo.</Tippy> | `React.ReactElement` | No | - |\n| **children** <Tippy>Navigation items to be rendered within the navigation bar, usually `LandingHeaderMenuItem`.</Tippy> | `React.ReactElement` | Yes | - |\n| **withBackground** <Tippy>Determines if the navigation bar should have a background.</Tippy> | `boolean` | No | `false` |\n| **variant** <Tippy>Defines the color variant of the background when `withBackground` is `true`.</Tippy> | `'primary' ǀ 'secondary'` | No | `'primary'` |\n| **fixed** <Tippy>Flag to determine if the navigation bar should be fixed at the top of the screen.</Tippy> | `boolean` | No | `false` |\n</PropsReference>"
            },
            "Newsletter": {
              "description": "This component displays a newsletter subscription form. On smaller screens, the input field may adjust accordingly to ensure usability.",
              "api": "<Usage>\n```jsx\nimport { LandingNewsletterSection } from '@/components/landing/newsletter/LandingNewsletterSection';\n```\n\n```jsx\n<LandingNewsletterSection\n  title=\"Never miss an update!\"\n  description=\"Subscribe to our newsletter to get the latest announcements, news and exclusive offers.\"\n>\n</LandingNewsletterSection>\n```\n</Usage>\n\n<Examples>\n### Text position\n\n\n```jsx\n<LandingNewsletterSection\n  title=\"Never miss an update!\"\n  description=\"Subscribe to our newsletter to get the latest announcements, news and exclusive offers.\"\n  textPosition=\"left\"\n>\n</LandingNewsletterSection>\n```\n\n\n\n### With avatar images\nShowing user images is a great way to add social proof to your newsletter section and increase conversions.\n\n\n\n```jsx\n<LandingNewsletterSection\n  title=\"Never miss an update!\"\n  description=\"Subscribe to our newsletter to get the latest announcements, news and exclusive offers.\"\n  withAvatars\n>\n</LandingNewsletterSection>\n```\n\n\n\n### With background\n\n\n```jsx\n<LandingNewsletterSection\n  title=\"Never miss an update!\"\n  description=\"Subscribe to our newsletter to get the latest announcements, news and exclusive offers.\"\n  withBackground\n>\n</LandingNewsletterSection>\n```\n\n\n\n### With background glow\n\n\n```jsx\n<LandingNewsletterSection\n  title=\"Never miss an update!\"\n  description=\"Subscribe to our newsletter to get the latest announcements, news and exclusive offers.\"\n  withBackgroundGlow\n>\n</LandingNewsletterSection>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name | Prop Type | Required | Default |\n| --------- | --------- | -------- | ------- |\n| **children** <Tippy>React nodes to be rendered within the component.</Tippy> | `React.ReactNode` ǀ `string` | No | - |\n| **innerClassName** <Tippy>Additional class names for the inner container.</Tippy> | `string` | No | - |\n| **title** <Tippy>The title text or React node.</Tippy> | `string` ǀ `React.ReactNode` | No | - |\n| **titleComponent** <Tippy>Optional React node to replace the title.</Tippy> | `React.ReactNode` | No | - |\n| **description** <Tippy>The description text or React node.</Tippy> | `string` ǀ `React.ReactNode` | No | - |\n| **descriptionComponent** <Tippy>Optional React node to replace the description.</Tippy> | `React.ReactNode` | No | - |\n| **buttonLabel** <Tippy>The label for the submit button.</Tippy> | `string` | No | `'Subscribe'` |\n| **placeholderLabel** <Tippy>The placeholder text for the email input.</Tippy> | `string` | No | `'Enter your email'` |\n| **inputLabel** <Tippy>The label for the email input.</Tippy> | `string` | No | `'Email address'` |\n| **textPosition** <Tippy>Position of the text inside the section.</Tippy> | `'center'` ǀ `'left'` | No | `'left'` |\n| **minHeight** <Tippy>Minimum height of the section.</Tippy> | `number` | No | `350` |\n| **withBackground** <Tippy>Whether to display a background.</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Whether to display a glowing background.</Tippy> | `boolean` | No | `false` |\n| **withAvatars** <Tippy>Whether to display user images.</Tippy> | `boolean` | No | `true` |\n| **variant** <Tippy>Determines the section's visual style.</Tippy> | `'primary'` ǀ `'secondary'` | No | `'primary'` |\n| **backgroundGlowVariant** <Tippy>Determines the glow background style.</Tippy> | `'primary'` ǀ `'secondary'` | No | `'primary'` |\n| **disabled** <Tippy>Whether to disable the component.</Tippy> | `boolean` | No | `false` |\n| **onSubmit** <Tippy>Callback function triggered on form submission.</Tippy> | `(e: React.FormEvent<HTMLFormElement>) => void` | No | `() => {}` |\n</PropsReference>"
            },
            "PricingComparison": {
              "description": "A versatile price comparison section that enables you to showcase feature differences across multiple products or services. Perfect for SaaS feature comparison pages, product comparisons, and feature matrices. The component supports 2-5 comparison columns, each with customizable headers, feature lists, and optional footers. Featured columns can be highlighted to draw attention to preferred options.",
              "api": "<Usage>\n```js\nimport {\n  LandingPriceComparisonSection,\n  LandingPriceComparisonColumn,\n  LandingPriceComparisonItem,\n} from '@/components/landing';\n```\n\n```jsx\n<LandingPriceComparisonSection\n  title=\"Product Comparison\"\n  description=\"Compare features across different plans\"\n>\n  <LandingPriceComparisonColumn\n    featured\n    header=\"Premium Plan\"\n    footer=\"$99/month\"\n  >\n    <LandingPriceComparisonItem\n      state=\"check\"\n      text=\"All features included\"\n    />\n  </LandingPriceComparisonColumn>\n</LandingPriceComparisonSection>\n```\n</Usage>\n\n<Examples>\n### Featured Column Highlighting & CTA\n\nAny column can be featured, but only one column can be featured at a time.\n\n\n\n```jsx\n<LandingPriceComparisonSection\n  title=\"Why choose us?\"\n  textPosition=\"center\"\n>\n  <LandingPriceComparisonColumn\n    header=\"Competitor A\"\n    footer=\"$129 /mo\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"All features\" />\n    <LandingPriceComparisonItem state=\"cross\" text=\"Premium support\" />\n  </LandingPriceComparisonColumn>\n\n  <LandingPriceComparisonColumn\n    featured\n    header=\"Our Product\"\n    footer=\"$19 /mo\"\n    ctaText=\"Get Started\"\n    href=\"#\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"All features\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Premium support\" />\n  </LandingPriceComparisonColumn>\n\n  <LandingPriceComparisonColumn\n    header=\"Competitor B\"\n    footer=\"$299 /mo\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"All features\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Premium support\" />\n  </LandingPriceComparisonColumn>\n</LandingPriceComparisonSection>\n```\n\n\n\n### With Tooltips and Descriptions\n\n\n\n```jsx\n<LandingPriceComparisonSection\n  title=\"Feature Comparison\"\n  description=\"Detailed comparison with helpful tooltips\"\n>\n  <LandingPriceComparisonColumn\n    featured\n    header=\"Professional\"\n    footer=\"$79 /mo\"\n    ctaText=\"Start Free Trial\"\n    href=\"#\"\n  >\n    <LandingPriceComparisonItem\n      state=\"check\"\n      text=\"Advanced Security\"\n      description=\"Enterprise-grade security with SOC 2 compliance\"\n    />\n    <LandingPriceComparisonItem\n      state=\"check\"\n      text=\"Priority Support\"\n      description=\"24/7 dedicated support with guaranteed response times\"\n    />\n  </LandingPriceComparisonColumn>\n\n  <LandingPriceComparisonColumn\n    header=\"Standard\"\n    footer=\"$39 /mo\"\n  >\n    <LandingPriceComparisonItem state=\"cross\" text=\"Advanced Security\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Priority Support\" />\n  </LandingPriceComparisonColumn>\n</LandingPriceComparisonSection>\n```\n\n\n\n### Secondary Variant\n\nThe secondary variant provides an alternative color scheme for the comparison section.\n\n\n\n```jsx\n<LandingPriceComparisonSection\n  title=\"Compare Plans\"\n  description=\"Choose the plan that's right for you\"\n  variant=\"secondary\"\n>\n  <LandingPriceComparisonColumn\n    featured\n    header=\"Pro\"\n    footer=\"$49 /mo\"\n    ctaText=\"Get Started\"\n    href=\"#\"\n    variant=\"secondary\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"Unlimited Projects\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Priority Support\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Advanced Analytics\" />\n  </LandingPriceComparisonColumn>\n\n\n  <LandingPriceComparisonColumn\n    header=\"Basic\"\n    footer=\"$19 /mo\"\n    variant=\"secondary\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"5 Projects\" />\n    <LandingPriceComparisonItem state=\"cross\" text=\"Priority Support\" />\n    <LandingPriceComparisonItem state=\"cross\" text=\"Advanced Analytics\" />\n  </LandingPriceComparisonColumn>\n\n  <LandingPriceComparisonColumn\n    header=\"Enterprise\"\n    footer=\"$99 /mo\"\n    variant=\"secondary\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"Unlimited Projects\" />\n    <LandingPriceComparisonItem state=\"cross\" text=\"Priority Support\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Advanced Analytics\" />\n  </LandingPriceComparisonColumn>\n</LandingPriceComparisonSection>\n```\n\n\n\n### Without Price\n\nPerfect for feature comparisons where pricing is not the focus.\n\n\n\n```jsx\n<LandingPriceComparisonSection\n  title=\"Feature Comparison\"\n  description=\"See how different solutions stack up\"\n>\n  <LandingPriceComparisonColumn\n    header=\"Solution A\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"API Access\" />\n    <LandingPriceComparisonItem state=\"cross\" text=\"Real-time Sync\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Basic Support\" />\n    <LandingPriceComparisonItem state=\"cross\" text=\"Custom Integrations\" />\n  </LandingPriceComparisonColumn>\n\n  <LandingPriceComparisonColumn\n    featured\n    header=\"Our Solution\"\n    ctaText=\"Learn More\"\n    href=\"#\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"API Access\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Real-time Sync\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Premium Support\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Custom Integrations\" />\n  </LandingPriceComparisonColumn>\n\n  <LandingPriceComparisonColumn\n    header=\"Solution B\"\n  >\n    <LandingPriceComparisonItem state=\"cross\" text=\"API Access\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Real-time Sync\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Basic Support\" />\n    <LandingPriceComparisonItem state=\"cross\" text=\"Custom Integrations\" />\n  </LandingPriceComparisonColumn>\n</LandingPriceComparisonSection>\n```\n\n\n\n### Without Button or footer\n\nClean comparison tables without call-to-action buttons for informational purposes.\n\n\n\n```jsx\n<LandingPriceComparisonSection\n  title=\"Product Specifications\"\n  description=\"Technical comparison of our products\"\n>\n  <LandingPriceComparisonColumn\n    header=\"Starter Edition\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"2 CPU Cores\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"4GB RAM\" />\n    <LandingPriceComparisonItem state=\"cross\" text=\"SSD Storage\" />\n    <LandingPriceComparisonItem state=\"cross\" text=\"24/7 Monitoring\" />\n  </LandingPriceComparisonColumn>\n\n  <LandingPriceComparisonColumn\n    header=\"Professional Edition\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"8 CPU Cores\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"16GB RAM\" />\n    <LandingPriceComparisonItem state=\"cross\" text=\"NVMe SSD Storage\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"24/7 Monitoring\" />\n  </LandingPriceComparisonColumn>\n\n  <LandingPriceComparisonColumn\n    featured\n    header=\"Enterprise Edition\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"4 CPU Cores\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"8GB RAM\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"SSD Storage\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"24/7 Monitoring\" />\n  </LandingPriceComparisonColumn>\n</LandingPriceComparisonSection>\n```\n\n\n\n### With Background\n\nAdds a subtle background to make the comparison section stand out from the rest of the page.\n\n\n\n```jsx\n<LandingPriceComparisonSection\n  title=\"Service Tiers\"\n  description=\"Choose the right level of service for your needs\"\n  withBackground\n>\n  <LandingPriceComparisonColumn\n    featured\n    header=\"Premium\"\n    footer=\"$79 /mo\"\n    ctaText=\"Upgrade Now\"\n    href=\"#\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"Priority Support\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"All Features\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Priority Queue\" />\n  </LandingPriceComparisonColumn>\n\n  <LandingPriceComparisonColumn\n    header=\"Enterprise\"\n    footer=\"Custom\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"Dedicated Support\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Custom Features\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Dedicated Infrastructure\" />\n  </LandingPriceComparisonColumn>\n</LandingPriceComparisonSection>\n```\n\n\n\n### With Background Glow\n\nCreates an eye-catching glow effect that draws attention to the comparison section.\n\n\n\n```jsx\n<LandingPriceComparisonSection\n  title=\"Platform Comparison\"\n  description=\"See why developers choose our platform\"\n  variant=\"secondary\"\n  withBackgroundGlow\n  backgroundGlowVariant=\"secondary\"\n>\n  <LandingPriceComparisonColumn\n    header=\"Other Platforms\"\n    footer=\"Variable pricing\"\n  >\n    <LandingPriceComparisonItem state=\"cross\" text=\"Easy Setup\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Basic Documentation\" />\n    <LandingPriceComparisonItem state=\"cross\" text=\"Modern UI Components\" />\n    <LandingPriceComparisonItem state=\"cross\" text=\"One-click Deploy\" />\n  </LandingPriceComparisonColumn>\n\n  <LandingPriceComparisonColumn\n    featured\n    header=\"Our Platform\"\n    footer=\"Free to start\"\n    ctaText=\"Try It Now\"\n    href=\"#\"\n  >\n    <LandingPriceComparisonItem state=\"check\" text=\"Easy Setup\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Comprehensive Docs\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"Modern UI Components\" />\n    <LandingPriceComparisonItem state=\"check\" text=\"One-click Deploy\" />\n  </LandingPriceComparisonColumn>\n</LandingPriceComparisonSection>\n```\n\n\n\n### Custom Icons and Content\n\n\n\n```jsx\n<LandingPriceComparisonSection title=\"Service Tiers\">\n  <LandingPriceComparisonColumn\n    header={\n      <div className=\"flex items-center gap-2\">\n        <LayersIcon className=\"w-6 h-6\" />\n        <span className='font-bold'>Competitor</span>\n      </div>\n    }\n    footer={\n      <div>\n        <div className=\"text-2xl font-bold\">$29</div>\n        <div className=\"text-sm text-gray-300\">per month</div>\n      </div>\n    }\n  >\n    <LandingPriceComparisonItem\n      icon={<div className=\"w-5 h-5 bg-red-500 rounded-full\"></div>}\n      text=\"5 Projects\"\n    />\n    <LandingPriceComparisonItem\n      icon={<div className=\"w-5 h-5 bg-red-500 rounded-full\"></div>}\n      text=\"Basic Support\"\n    />\n  </LandingPriceComparisonColumn>\n\n  <LandingPriceComparisonColumn\n    featured\n    header={\n      <div className=\"flex items-center gap-2\">\n        <SparklesIcon className=\"w-6 h-6\" />\n        <span className='font-bold'>Our Product</span>\n      </div>\n    }\n    footer={\n      <div>\n        <div className=\"text-2xl font-bold\">$29</div>\n        <div className=\"text-sm text-gray-300\">per month</div>\n      </div>\n    }\n    ctaTextComponent={\n      <Button variant=\"outlineSecondary\">\n        <span>Get Started</span>\n      </Button>\n    }\n  >\n    <LandingPriceComparisonItem\n      icon={<div className=\"w-5 h-5 bg-green-500 rounded-full\"></div>}\n      text=\"10 projects\"\n    />\n    <LandingPriceComparisonItem\n      icon={<div className=\"w-5 h-5 bg-green-500 rounded-full\"></div>}\n      text=\"Priority Support\"\n    />\n  </LandingPriceComparisonColumn>\n</LandingPriceComparisonSection>\n```\n\n\n</Examples>\n\n<PropsReference>\n### LandingPriceComparisonSection\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **children** <Tippy>The comparison columns to display</Tippy> | `React.ReactNode` | Yes | - |\n| **className** <Tippy>Additional CSS classes for styling</Tippy> | `string` | No | - |\n| **title** <Tippy>Main heading for the comparison section</Tippy> | `string \\| React.ReactNode` | No | - |\n| **titleComponent** <Tippy>Custom title component override</Tippy> | `React.ReactNode` | No | - |\n| **description** <Tippy>Subtitle text below the main heading</Tippy> | `string \\| React.ReactNode` | No | - |\n| **descriptionComponent** <Tippy>Custom description component override</Tippy> | `React.ReactNode` | No | - |\n| **textPosition** <Tippy>Alignment of title and description text</Tippy> | `'center' \\| 'left'` | No | `'center'` |\n| **withBackground** <Tippy>Adds a subtle background color to the section</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Adds a glow effect background</Tippy> | `boolean` | No | `false` |\n| **variant** <Tippy>Color scheme variant for backgrounds</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **backgroundGlowVariant** <Tippy>Color variant for the glow effect</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n\n### LandingPriceComparisonColumn\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **children** <Tippy>The comparison items or content to display</Tippy> | `React.ReactNode` | Yes | - |\n| **className** <Tippy>Additional CSS classes for styling</Tippy> | `string` | No | - |\n| **header** <Tippy>Header content (usually product/service name)</Tippy> | `string \\| React.ReactNode` | No | - |\n| **headerComponent** <Tippy>Custom header component override</Tippy> | `React.ReactNode` | No | - |\n| **footer** <Tippy>Footer content (usually pricing information)</Tippy> | `string \\| React.ReactNode` | No | - |\n| **footerComponent** <Tippy>Custom footer component override</Tippy> | `React.ReactNode` | No | - |\n| **featured** <Tippy>Highlights this column as the featured/recommended option</Tippy> | `boolean` | No | `false` |\n| **ctaText** <Tippy>Text for the call-to-action button</Tippy> | `string` | No | - |\n| **ctaTextComponent** <Tippy>Custom call-to-action button component</Tippy> | `React.ReactNode` | No | - |\n| **href** <Tippy>URL for the call-to-action button link</Tippy> | `string` | No | - |\n| **onClick** <Tippy>Click handler for the call-to-action button</Tippy> | `() => void` | No | - |\n| **variant** <Tippy>Color scheme variant for backgrounds</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n\n### LandingPriceComparisonItem\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **className** <Tippy>Additional CSS classes for styling</Tippy> | `string` | No | - |\n| **icon** <Tippy>Custom icon to display (overrides state-based icons)</Tippy> | `React.ReactNode` | No | - |\n| **iconComponent** <Tippy>Alternative way to pass custom icon component</Tippy> | `React.ReactNode` | No | - |\n| **text** <Tippy>The feature text to display</Tippy> | `string \\| React.ReactNode` | No | - |\n| **textComponent** <Tippy>Alternative way to pass custom text component</Tippy> | `React.ReactNode` | No | - |\n| **description** <Tippy>Additional details shown in tooltip on hover</Tippy> | `string \\| React.ReactNode` | No | - |\n| **descriptionComponent** <Tippy>Custom description component for tooltip</Tippy> | `React.ReactNode` | No | - |\n| **state** <Tippy>Predefined state that determines icon and styling</Tippy> | `'check' \\| 'cross' \\| 'neutral' \\| 'custom'` | No | `'neutral'` |\n| **showText** <Tippy>Whether to display the text content visibly</Tippy> | `boolean` | No | `false` |\n| **showDescription** <Tippy>Whether to show the info icon with tooltip</Tippy> | `boolean` | No | `false` |\n</PropsReference>"
            },
            "PricingPlan": {
              "description": "This component displays a pricing plan (tier) with features and a call-to-action button. On smaller screens, the layout becomes a single column to ensure usability. Meant to be used as a child of: [Landing Page Pricing Section](/boilerplate-documentation/landing-page-components/pricing)",
              "api": "<Usage>\n```jsx\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n```\n\n```jsx\n<LandingPricingPlan\n  title=\"Pro\"\n  description=\"For larger teams or businesses.\"\n  ctaText=\"Upgrade now\"\n  price=\"$20\"\n  priceSuffix=\"/mo\"\n  highlighted\n>\n  <p>Unlimited users</p>\n  <p>AI features</p>\n  <p>Priority support</p>\n</LandingPricingPlan>\n```\n</Usage>\n\n<Examples>\n### With Price suffix\nFor some pricing plans, you may want to add a suffix to the price, like /mo or /year etc.\n\n\n\n```jsx\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingPlan\n  title=\"Pro\"\n  description=\"For larger teams or businesses.\"\n  ctaText=\"Upgrade now\"\n  price=\"$20\"\n  priceSuffix=\"/mo\"\n>\n  <p>Unlimited users</p>\n  <p>AI features</p>\n  <p>Priority support</p>\n</LandingPricingPlan>\n```\n\n\n\n### With highlighted plan\n\n\n```jsx\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingPlan\n  title=\"Pro\"\n  description=\"For larger teams or businesses.\"\n  ctaText=\"Upgrade now\"\n  price=\"$20\"\n  highlighted\n>\n  <p>Unlimited users</p>\n  <p>AI features</p>\n  <p>Priority support</p>\n</LandingPricingPlan>\n```\n\n\n\n### With featured plan\nFeatured plans are meant to stand out and are usually used for more expensive / enterprise plans.\n\n\n\n```jsx\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingPlan\n  title=\"Pro\"\n  description=\"For larger teams or businesses.\"\n  ctaText=\"Upgrade now\"\n  price=\"$20\"\n  featured\n>\n  <p>Unlimited users</p>\n  <p>AI features</p>\n  <p>Priority support</p>\n</LandingPricingPlan>\n```\n\n\n\n### With discount\n\n\n```jsx\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingPlan\n  title=\"Pro\"\n  description=\"For larger teams or businesses.\"\n  ctaText=\"Upgrade now\"\n  price=\"$20\"\n  discountPrice=\"$10\"\n>\n  <p>Unlimited users</p>\n  <p>AI features</p>\n  <p>Priority support</p>\n</LandingPricingPlan>\n```\n\n\n\n### Sold out\n\n\n```jsx\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingPlan\n  title=\"Pro\"\n  description=\"For larger teams or businesses.\"\n  ctaText=\"Upgrade now\"\n  price=\"$20\"\n  soldOut\n>\n  <p>Unlimited users</p>\n  <p>AI features</p>\n  <p>Priority support</p>\n</LandingPricingPlan>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name | Prop Type | Required | Default |\n| --------- | --------- | -------- | ------- |\n| **children** <Tippy>React nodes to be rendered within the component.</Tippy> | `React.ReactNode` | Yes | - |\n| **title** <Tippy>The title of the pricing plan.</Tippy> | `string` | No | - |\n| **titleComponent** <Tippy>Custom React node to replace the title.</Tippy> | `React.ReactNode` | No | - |\n| **description** <Tippy>A brief description of the pricing plan.</Tippy> | `string` | No | - |\n| **descriptionComponent** <Tippy>Custom React node to replace the description.</Tippy> | `React.ReactNode` | No | - |\n| **href** <Tippy>The link for the call-to-action button.</Tippy> | `string` | No | `\"#\"` |\n| **onClick** <Tippy>Click handler for the call-to-action button.</Tippy> | `() => void` | No | `() => {}` |\n| **ctaText** <Tippy>Text displayed on the call-to-action button.</Tippy> | `string` | No | `\"Get started\"` |\n| **price** <Tippy>The main price of the plan.</Tippy> | `string` | Yes | - |\n| **discountPrice** <Tippy>A discounted price if applicable.</Tippy> | `string` | No | - |\n| **priceSuffix** <Tippy>Suffix text displayed after the price.</Tippy> | `string` | No | - |\n| **featured** <Tippy>Marks the plan as featured, affecting styling.</Tippy> | `boolean` | No | - |\n| **highlighted** <Tippy>Highlights the plan visually.</Tippy> | `boolean` | No | - |\n| **soldOut** <Tippy>Disables the call-to-action button if the plan is sold out.</Tippy> | `boolean` | No | - |\n</PropsReference>"
            },
            "Pricing": {
              "description": "This component displays different pricing tiers with features and a call-to-action button. On smaller screens, the layout becomes a single column to ensure usability. Also see: [Landing Page Pricing Plan](/boilerplate-documentation/landing-page-components/pricing-plan)",
              "api": "<Usage>\n```jsx\nimport { LandingPricingSection } from '@/components/landing/pricing/LandingPricingSection';\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n```\n\n```jsx\n<LandingPricingSection\n  title=\"Simple, scalable pricing\"\n  description=\"Affordable pricing plans tailored to your needs. Choose a plan that works best for you.\"\n>\n  <LandingPricingPlan\n    title=\"Free\"\n    description=\"For small teams & personal use.\"\n    price=\"$0\"\n  >\n    <p>Up to 5 users</p>\n    <p>Basic features</p>\n    <p>Discord access</p>\n  </LandingPricingPlan>\n\n  <LandingPricingPlan\n    title=\"Pro\"\n    description=\"For larger teams or businesses.\"\n    ctaText=\"Upgrade now\"\n    price=\"$20\"\n    priceSuffix=\"/mo\"\n    highlighted\n  >\n    <p>Unlimited users</p>\n    <p>AI features</p>\n    <p>Priority support</p>\n  </LandingPricingPlan>\n</LandingPricingSection>\n```\n</Usage>\n\n<Examples>\n### Text position\n\n\n```jsx\nimport { LandingPricingSection } from '@/components/landing/pricing/LandingPricingSection';\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingSection\n  title=\"Simple, scalable pricing\"\n  description=\"Affordable pricing plans tailored to your needs. Choose a plan that works best for you.\"\n  textPosition=\"left\"\n>\n  <LandingPricingPlan\n    title=\"Free\"\n    description=\"For small teams & personal use.\"\n    price=\"$0\"\n  >\n    <p>Up to 5 users</p>\n    <p>Basic features</p>\n    <p>Discord access</p>\n  </LandingPricingPlan>\n\n  <LandingPricingPlan\n    title=\"Pro\"\n    description=\"For larger teams or businesses.\"\n    ctaText=\"Upgrade now\"\n    price=\"$20\"\n  >\n    <p>Unlimited users</p>\n    <p>AI features</p>\n    <p>Priority support</p>\n  </LandingPricingPlan>\n</LandingPricingSection>\n```\n\n\n\n### With Price suffix\nFor some pricing plans, you may want to add a suffix to the price, like /mo or /year etc.\n\n\n\n```jsx\nimport { LandingPricingSection } from '@/components/landing/pricing/LandingPricingSection';\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingSection\n  title=\"Simple, scalable pricing\"\n  description=\"Affordable pricing plans tailored to your needs. Choose a plan that works best for you.\"\n>\n  <LandingPricingPlan\n    title=\"Free\"\n    description=\"For small teams & personal use.\"\n    price=\"$0\"\n    priceSuffix=\"/forever\"\n  >\n    <p>Up to 5 users</p>\n    <p>Basic features</p>\n    <p>Discord access</p>\n  </LandingPricingPlan>\n\n  <LandingPricingPlan\n    title=\"Pro\"\n    description=\"For larger teams or businesses.\"\n    ctaText=\"Upgrade now\"\n    price=\"$20\"\n    priceSuffix=\"/mo\"\n  >\n    <p>Unlimited users</p>\n    <p>AI features</p>\n    <p>Priority support</p>\n  </LandingPricingPlan>\n</LandingPricingSection>\n```\n\n\n\n### With highlighted plan\n\n\n```jsx\nimport { LandingPricingSection } from '@/components/landing/pricing/LandingPricingSection';\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingSection\n  title=\"Simple, scalable pricing\"\n  description=\"Affordable pricing plans tailored to your needs. Choose a plan that works best for you.\"\n>\n  <LandingPricingPlan\n    title=\"Free\"\n    description=\"For small teams & personal use.\"\n    price=\"$0\"\n  >\n    <p>Up to 5 users</p>\n    <p>Basic features</p>\n    <p>Discord access</p>\n  </LandingPricingPlan>\n\n  <LandingPricingPlan\n    title=\"Pro\"\n    description=\"For larger teams or businesses.\"\n    ctaText=\"Upgrade now\"\n    price=\"$20\"\n    highlighted\n  >\n    <p>Unlimited users</p>\n    <p>AI features</p>\n    <p>Priority support</p>\n  </LandingPricingPlan>\n</LandingPricingSection>\n```\n\n\n\n### With featured plan\nFeatured plans are meant to stand out and are usually used for more expensive / enterprise plans.\n\n\n\n```jsx\nimport { LandingPricingSection } from '@/components/landing/pricing/LandingPricingSection';\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingSection\n  title=\"Simple, scalable pricing\"\n  description=\"Affordable pricing plans tailored to your needs. Choose a plan that works best for you.\"\n>\n  <LandingPricingPlan\n    title=\"Free\"\n    description=\"For small teams & personal use.\"\n    price=\"$0\"\n  >\n    <p>Up to 5 users</p>\n    <p>Basic features</p>\n    <p>Discord access</p>\n  </LandingPricingPlan>\n\n  <LandingPricingPlan\n    title=\"Pro\"\n    description=\"For larger teams or businesses.\"\n    ctaText=\"Upgrade now\"\n    price=\"$20\"\n    featured\n  >\n    <p>Unlimited users</p>\n    <p>AI features</p>\n    <p>Priority support</p>\n  </LandingPricingPlan>\n</LandingPricingSection>\n```\n\n\n\n### With discount\n\n\n```jsx\nimport { LandingPricingSection } from '@/components/landing/pricing/LandingPricingSection';\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingSection\n  title=\"Simple, scalable pricing\"\n  description=\"Affordable pricing plans tailored to your needs. Choose a plan that works best for you.\"\n>\n  <LandingPricingPlan\n    title=\"Free\"\n    description=\"For small teams & personal use.\"\n    price=\"$0\"\n  >\n    <p>Up to 5 users</p>\n    <p>Basic features</p>\n    <p>Discord access</p>\n  </LandingPricingPlan>\n\n  <LandingPricingPlan\n    title=\"Pro\"\n    description=\"For larger teams or businesses.\"\n    ctaText=\"Upgrade now\"\n    price=\"$20\"\n    discountPrice=\"$10\"\n  >\n    <p>Unlimited users</p>\n    <p>AI features</p>\n    <p>Priority support</p>\n  </LandingPricingPlan>\n</LandingPricingSection>\n```\n\n\n\n### Sold out\n\n\n```jsx\nimport { LandingPricingSection } from '@/components/landing/pricing/LandingPricingSection';\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingSection\n  title=\"Simple, scalable pricing\"\n  description=\"Affordable pricing plans tailored to your needs. Choose a plan that works best for you.\"\n>\n  <LandingPricingPlan\n    title=\"Free\"\n    description=\"For small teams & personal use.\"\n    price=\"$0\"\n  >\n    <p>Up to 5 users</p>\n    <p>Basic features</p>\n    <p>Discord access</p>\n  </LandingPricingPlan>\n\n  <LandingPricingPlan\n    title=\"Pro\"\n    description=\"For larger teams or businesses.\"\n    ctaText=\"Upgrade now\"\n    price=\"$20\"\n    soldOut\n  >\n    <p>Unlimited users</p>\n    <p>AI features</p>\n    <p>Priority support</p>\n  </LandingPricingPlan>\n</LandingPricingSection>\n```\n\n\n\n### Multiple columns\nYou can use the pricing section to display between 1 to 4 pricing plans. The layout will automatically adjust based on the number of plans you provide.\n\n\n\n```jsx\nimport { LandingPricingSection } from '@/components/landing/pricing/LandingPricingSection';\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingSection\n  title=\"Simple, scalable pricing\"\n  description=\"Affordable pricing plans tailored to your needs. Choose a plan that works best for you.\"\n>\n  <LandingPricingPlan\n    title=\"Free\"\n    description=\"For small teams & personal use.\"\n    price=\"$0\"\n  >\n    <p>Up to 5 users</p>\n    <p>Basic features</p>\n    <p>Discord access</p>\n  </LandingPricingPlan>\n\n  <LandingPricingPlan\n    title=\"Pro\"\n    description=\"For larger teams or businesses.\"\n    ctaText=\"Upgrade\"\n    price=\"$20\"\n    priceSuffix=\"/mo\"\n    highlighted\n  >\n    <p>Unlimited users</p>\n    <p>AI features</p>\n    <p>Priority support</p>\n  </LandingPricingPlan>\n\n  <LandingPricingPlan\n    title=\"Enterprise\"\n    description=\"For enterprise teams & businesses.\"\n    ctaText=\"Sign up\"\n    price=\"$100\"\n    priceSuffix=\"/mo\"\n    featured\n  >\n    <p>Unlimited users</p>\n    <p>AI features</p>\n    <p>Priority support</p>\n  </LandingPricingPlan>\n</LandingPricingSection>\n```\n\n\n\n\n### With background\n\n\n```jsx\nimport { LandingPricingSection } from '@/components/landing/pricing/LandingPricingSection';\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingSection\n  title=\"Simple, scalable pricing\"\n  description=\"Affordable pricing plans tailored to your needs. Choose a plan that works best for you.\"\n  withBackground\n>\n  <LandingPricingPlan\n    title=\"Free\"\n    description=\"For small teams & personal use.\"\n    price=\"$0\"\n  >\n    <p>Up to 5 users</p>\n    <p>Basic features</p>\n    <p>Discord access</p>\n  </LandingPricingPlan>\n\n  <LandingPricingPlan\n    title=\"Pro\"\n    description=\"For larger teams or businesses.\"\n    ctaText=\"Upgrade now\"\n    price=\"$20\"\n  >\n    <p>Unlimited users</p>\n    <p>AI features</p>\n    <p>Priority support</p>\n  </LandingPricingPlan>\n</LandingPricingSection>\n```\n\n\n\n### With background glow\n\n\n```jsx\nimport { LandingPricingSection } from '@/components/landing/pricing/LandingPricingSection';\nimport { LandingPricingPlan } from '@/components/landing/pricing/LandingPricingPlan';\n\n<LandingPricingSection\n  title=\"Simple, scalable pricing\"\n  description=\"Affordable pricing plans tailored to your needs. Choose a plan that works best for you.\"\n  withBackgroundGlow\n>\n  <LandingPricingPlan\n    title=\"Free\"\n    description=\"For small teams & personal use.\"\n    price=\"$0\"\n  >\n    <p>Up to 5 users</p>\n    <p>Basic features</p>\n    <p>Discord access</p>\n  </LandingPricingPlan>\n\n  <LandingPricingPlan\n    title=\"Pro\"\n    description=\"For larger teams or businesses.\"\n    ctaText=\"Upgrade now\"\n    price=\"$20\"\n  >\n    <p>Unlimited users</p>\n    <p>AI features</p>\n    <p>Priority support</p>\n  </LandingPricingPlan>\n</LandingPricingSection>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name | Prop Type | Required | Default |\n| --------- | --------- | -------- | ------- |\n| **children** <Tippy>React nodes to be rendered within the component (below the text on smaller screen, right side on larger).</Tippy> | `React.ReactNode` | No | - |\n| **title** <Tippy>The main title text or React node.</Tippy> | `string` ǀ `React.ReactNode` | No | - |\n| **titleComponent** <Tippy>A React node to render as the title instead of text.</Tippy> | `React.ReactNode` | No | - |\n| **description** <Tippy>The description text or React node.</Tippy> | `string` ǀ `React.ReactNode` | No | - |\n| **descriptionComponent** <Tippy>A React node to render as the description instead of text.</Tippy> | `React.ReactNode` | No | - |\n| **textPosition** <Tippy>Position of the text content.</Tippy> | `'center'` ǀ `'left'` | No | `'center'` |\n| **withBackground** <Tippy>Whether to display a background.</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Whether to add a glowing background effect.</Tippy> | `boolean` | No | `false` |\n| **withAvatars** <Tippy>Whether to display avatars.</Tippy> | `boolean` | No | `false` |\n| **variant** <Tippy>The visual style of the component.</Tippy> | `'primary'` ǀ `'secondary'` | No | `'primary'` |\n| **backgroundGlowVariant** <Tippy>The variant of the glowing background effect.</Tippy> | `'primary'` ǀ `'secondary'` | No | `'primary'` |\n</PropsReference>"
            },
            "PrimaryCtaEffects": {
              "description": "A collection of animated background effects for the primary CTA section. These components are meant to be used together with `LandingPrimaryImageCtaSection`, `LandingPrimaryTextCtaSection` and `LandingPrimaryVideoCtaSection`. On a landing page, they can make it stand out and make it more memorable and depending on the background effect - more engaging. ## `LandingDotParticleCtaBg` **Dot Particle with CTA**",
              "api": ""
            },
            "PrimaryCtaTextEffects": {
              "description": "A collection of text effects for the primary CTA section. These are meant to be used together with `LandingPrimaryImageCtaSection`, `LandingPrimaryTextCtaSection` and `LandingPrimaryVideoCtaSection`, as part of the `titleComponent` prop. On a landing page, they can be used to emphasize parts of the CTA title and increase conversion rates. There are not components per say, but a collection of patterns using Tailwind CSS effects and text effects. ## Monochrome Gradient Text **Monochrome Gradient Text**<br/> This works with `primary`, `secondary` and Tailwind color schemes.",
              "api": ""
            },
            "PrimaryImageCta": {
              "description": "Use this component on landing page as the primary Call to Action section. A section that shows a title & description. Optionally, it can have actions (children), leading components and a background glow. This is the most important section of your landing page. Use it to grab the attention of your visitors and encourage them to take action.",
              "api": "<Usage>\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\n```\n\n```jsx\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n</LandingPrimaryImageCtaSection>\n```\n</Usage>\n\n<Examples>\n### Image Perspective\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  imagePerspective=\"paper\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n</LandingPrimaryImageCtaSection>;\n```\n\n\n\n### Image Position\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  imagePosition=\"center\"\n  textPosition=\"center\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n</LandingPrimaryImageCtaSection>;\n```\n\n\n\n### With Social Proof\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  imagePosition=\"center\"\n  textPosition=\"center\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingSocialProof\n    className=\"mt-6 w-full flex justify-center\"\n    showRating\n    numberOfUsers={99}\n    avatarItems={[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Doe',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alice Doe',\n      },\n    ]}\n  />\n</LandingPrimaryImageCtaSection>;\n```\n\n\n\n### With Discount/Offer\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  imagePosition=\"center\"\n  textPosition=\"center\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full flex justify-center\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryImageCtaSection>;\n```\n\n\n\n### With Discount and Left Alignment\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryImageCtaSection>;\n```\n\n\n\n### With Bullet Points\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductFeatureKeyPoints } from '@/components/landing/LandingProductFeatureKeyPoints';\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  descriptionComponent={\n    <LandingProductFeatureKeyPoints\n      keyPoints={[\n        {\n          title: 'Intelligent Assistance',\n          description:\n            'Receive personalized recommendations and insights tailored to your workflow.',\n        },\n        {\n          title: 'Seamless Collaboration',\n          description:\n            'Easily collaborate with team members and clients in real-time.',\n        },\n        {\n          title: 'Advanced Customization',\n          description:\n            'Tailor your app to fit your unique requirements with extensive customization options.',\n        },\n      ]}\n    />\n  }\n  textPosition=\"left\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryImageCtaSection>;\n```\n\n\n\n### With Product Hunt Award\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  leadingComponent={<LandingProductHuntAward />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryImageCtaSection>;\n```\n\n\n\n### With Social Proof Band\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\nimport { LandingSocialProofBand } from '@/components/landing/social-proof/LandingSocialProofBand';\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<LandingSocialProofBand>\n  <LandingSocialProofBandItem>\n    100% encrypted and secure\n  </LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>24/7 customer support</LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>\n    99% customer satisfaction\n  </LandingSocialProofBandItem>\n</LandingSocialProofBand>\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  leadingComponent={<LandingProductHuntAward />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">\n      Buy now\n    </a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryImageCtaSection>\n```\n\n\n\n### With Background\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  withBackground\n  variant=\"secondary\"\n  leadingComponent={<LandingProductHuntAward />}\n>\n  <Button size=\"xl\" variant=\"secondary\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlineSecondary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryImageCtaSection>;\n```\n\n\n\n### With Background Glow\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  withBackground\n  withBackgroundGlow\n  leadingComponent={<LandingProductHuntAward />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryImageCtaSection>;\n```\n\n\n\n### Left-aligned Full Example\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\nimport { LandingSocialProofBand } from '@/components/landing/social-proof/LandingSocialProofBand';\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<LandingSocialProofBand>\n  <LandingSocialProofBandItem>\n    100% encrypted and secure\n  </LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>24/7 customer support</LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>\n    99% customer satisfaction\n  </LandingSocialProofBandItem>\n</LandingSocialProofBand>\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  withBackground\n  withBackgroundGlow\n  leadingComponent={<LandingProductHuntAward />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">\n      Buy now\n    </a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingSocialProof\n    className=\"w-full mt-12\"\n    showRating\n    numberOfUsers={99}\n    avatarItems={[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Doe',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alice Doe',\n      },\n    ]}\n  />\n</LandingPrimaryImageCtaSection>\n```\n\n\n\n### Centered full example\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\nimport { LandingSocialProofBand } from '@/components/landing/social-proof/LandingSocialProofBand';\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<LandingSocialProofBand invert>\n  <LandingSocialProofBandItem>\n    100% encrypted and secure\n  </LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>24/7 customer support</LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>\n    99% customer satisfaction\n  </LandingSocialProofBandItem>\n</LandingSocialProofBand>\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  imagePosition=\"center\"\n  textPosition=\"center\"\n  withBackground\n  withBackgroundGlow\n  variant=\"secondary\"\n  backgroundGlowVariant=\"secondary\"\n  leadingComponent={<LandingProductHuntAward />}\n>\n  <Button size=\"xl\" variant=\"secondary\" asChild>\n    <a href=\"#\">\n      Buy now\n    </a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlineSecondary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full flex justify-center\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n\n  <LandingSocialProof\n    className=\"mt-12 w-full flex justify-center\"\n    showRating\n    numberOfUsers={99}\n    avatarItems={[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Doe',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alice Doe',\n      },\n    ]}\n  />\n</LandingPrimaryImageCtaSection>\n```\n\n\n\n### With Newsletter Form\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingNewsletterInput } from '@/components/landing/newsletter/LandingNewsletterInput';\n\n<LandingPrimaryImageCtaSection\n  title=\"Sign up now\"\n  description=\"Never miss an update! Subscribe today for the latest announcements.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n>\n  <LandingNewsletterInput />\n</LandingPrimaryImageCtaSection>\n```\n\n\n\n\n### With Newsletter and Social Proof\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\nimport { LandingNewsletterInput } from '@/components/landing/newsletter/LandingNewsletterInput';\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  imagePosition=\"center\"\n  textPosition=\"center\"\n>\n  <LandingNewsletterInput className=\"max-w-xs\" />\n\n  <LandingSocialProof\n    className=\"mt-6 w-full flex justify-center\"\n    showRating\n    numberOfUsers={99}\n    avatarItems={[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Doe',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alice Doe',\n      },\n    ]}\n  />\n</LandingPrimaryImageCtaSection>;\n```\n\n\n\n### With Background Effects\n\nMore effects are available in the <a href=\"/boilerplate-documentation/landing-page-components/primary-cta-effects\" className=\"fancy-link\">Primary CTA Background Effects</a> section.\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDotParticleCtaBg } from '@/components/landing/cta-backgrounds/LandingDotParticleCtaBg';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\n\n<LandingPrimaryImageCtaSection\n  title=\"Revolutionary Product Launch\"\n  description=\"Experience the future with our cutting-edge solution. Built for modern teams who demand excellence and reliability.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Product showcase\"\n  textPosition=\"center\"\n  imagePosition=\"center\"\n  effectComponent={<LandingDotParticleCtaBg />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Start Free Trial</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlineSecondary\">\n    <a href=\"#\">Watch Demo</a>\n  </Button>\n\n  <LandingSocialProof\n    className=\"mt-8 w-full flex justify-center\"\n    showRating\n    numberOfUsers={1000}\n    avatarItems={[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Smith',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alex Johnson',\n      },\n    ]}\n  />\n</LandingPrimaryImageCtaSection>\n```\n\n\n\n### With Leading Pill\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingLeadingPill } from '@/components/landing/leading/LandingLeadingPill';\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  imagePosition=\"center\"\n  leadingComponent={<LandingLeadingPill\n    text=\"Best generator\"\n    borderVariant=\"primary\"\n  />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n</LandingPrimaryImageCtaSection>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                             | Prop Type                                                                | Required | Default     |\n| ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | -------- | ----------- |\n| **children** <Tippy>React nodes to be rendered within the component (below the text on smaller screen, right side on larger).</Tippy> | `React.ReactNode` ǀ `string`                                             | Yes      | -           |\n| **title** <Tippy>The title to be displayed in the component.</Tippy>                                                                  | `string` ǀ `React.ReactNode`                                             | Yes      | -           |\n| **description** <Tippy>The description to be displayed in the component.</Tippy>                                                      | `string` ǀ `React.ReactNode`                                             | Yes      | -           |\n| **innerClassName** <Tippy>Additional CSS class to be applied to the inner container of the component.</Tippy>                         | `string`                                                                 | No       | -           |\n| **titleComponent** <Tippy>Custom component for rendering the title.</Tippy>                                                           | `React.ReactNode`                                                        | No       | -           |\n| **descriptionComponent** <Tippy>Custom component for rendering the description.</Tippy>                                               | `React.ReactNode`                                                        | No       | -           |\n| **leadingComponent** <Tippy>Custom component to be rendered before the title and description.</Tippy>                                 | `React.ReactNode`                                                        | No       | -           |\n| **footerComponent** <Tippy>Custom component to be rendered as the footer of the section.</Tippy>                                      | `React.ReactNode`                                                        | No       | -           |\n| **textPosition** <Tippy>The position of text content within the component.</Tippy>                                                    | `'center'` ǀ `'left'`                                                    | No       | `'left'`    |\n| **imageSrc** <Tippy>The source URL for the image to be displayed in the component.</Tippy>                                            | `string`                                                                 | No       | -           |\n| **imageAlt** <Tippy>The alt text for the image.</Tippy>                                                                               | `string`                                                                 | No       | `''`        |\n| **imagePosition** <Tippy>The position of the image within the component.</Tippy>                                                      | `'left'` ǀ `'right'` ǀ `'center'`                                        | No       | `'right'`   |\n| **imagePerspective** <Tippy>The perspective effect applied to the image.</Tippy>                                                      | `'none'` ǀ `'left'` ǀ `'right'` ǀ `'bottom'` ǀ `'bottom-lg'` ǀ `'paper'` | No       | `'none'`    |\n| **imageShadow** <Tippy>The type of shadow applied to the image.</Tippy>                                                               | `'none'` ǀ `'soft'` ǀ `'hard'`                                           | No       | `'hard'`    |\n| **minHeight** <Tippy>The minimum height of the section containing the component.</Tippy>                                              | `number`                                                                 | No       | `350`       |\n| **withBackground** <Tippy>Whether to display a background for the component.</Tippy>                                                  | `boolean`                                                                | No       | `false`     |\n| **withBackgroundGlow** <Tippy>Whether to display a glowing background for the component.</Tippy>                                      | `boolean`                                                                | No       | `false`     |\n| **variant** <Tippy>The variant style for the component.</Tippy>                                                                       | `'primary'` ǀ `'secondary'`                                              | No       | `'primary'` |\n| **backgroundGlowVariant** <Tippy>The variant style for the glowing background.</Tippy>                                                | `'primary'` ǀ `'secondary'`                                              | No       | `'primary'` |\n| **effectComponent** <Tippy>Custom React component to be displayed as the background effect.</Tippy>                                    | `React.ReactNode`                                                        | No       | -           |\n| **effectClassName** <Tippy>Additional CSS classes to apply to the background effect.</Tippy>                                        | `string`                                                                 | No       | -           |\n</PropsReference>"
            },
            "PrimaryTextCta": {
              "description": "Use this component on landing page as the primary Call to Action section. A section that shows a title & description. Optionally, it can have actions (children) and a background. This is the most important section of your landing page. Use it to grab the attention of your visitors and encourage them to take action.",
              "api": "<Usage>\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryTextCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\n```\n\n```jsx\n<LandingPrimaryTextCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n</LandingPrimaryTextCtaSection>\n```\n</Usage>\n\n<Examples>\n### With Social Proof\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryTextCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\n\n<LandingPrimaryTextCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingSocialProof\n    className=\"mt-6 w-full flex justify-center\"\n    showRating\n    numberOfUsers={99}\n    avatarItems={[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Doe',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alice Doe',\n      },\n    ]}\n  />\n</LandingPrimaryTextCtaSection>;\n```\n\n\n\n### With Discount/Offer\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryTextCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\n\n<LandingPrimaryTextCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full flex justify-center\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryTextCtaSection>;\n```\n\n\n\n### With Discount & Left Alignment\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryTextCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\n\n<LandingPrimaryTextCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  textPosition=\"left\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryTextCtaSection>;\n```\n\n\n\n### With Bullet Points\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryTextCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductFeatureKeyPoints } from '@/components/landing/LandingProductFeatureKeyPoints';\n\n<LandingPrimaryTextCtaSection\n  title=\"Landing page in minutes\"\n  descriptionComponent={\n    <LandingProductFeatureKeyPoints\n      keyPoints={[\n        {\n          title: 'Intelligent Assistance',\n          description:\n            'Receive personalized recommendations and insights tailored to your workflow.',\n        },\n        {\n          title: 'Seamless Collaboration',\n          description:\n            'Easily collaborate with team members and clients in real-time.',\n        },\n        {\n          title: 'Advanced Customization',\n          description:\n            'Tailor your app to fit your unique requirements with extensive customization.',\n        },\n      ]}\n    />\n  }\n  textPosition=\"left\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryTextCtaSection>;\n```\n\n\n\n### With Product Hunt Award\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryTextCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\n\n<LandingPrimaryTextCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  textPosition=\"left\"\n  leadingComponent={<LandingProductHuntAward />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryTextCtaSection>;\n```\n\n\n\n### With Social Proof Band\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryTextCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\nimport { LandingSocialProofBand } from '@/components/landing/social-proof/LandingSocialProofBand';\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<LandingSocialProofBand>\n  <LandingSocialProofBandItem>\n    100% encrypted and secure\n  </LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>24/7 customer support</LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>\n    99% customer satisfaction\n  </LandingSocialProofBandItem>\n</LandingSocialProofBand>\n\n<LandingPrimaryTextCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  textPosition=\"left\"\n  leadingComponent={<LandingProductHuntAward />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">\n      Buy now\n    </a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryTextCtaSection>\n```\n\n\n\n### With Background\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryTextCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\n\n<LandingPrimaryTextCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  textPosition=\"left\"\n  withBackground\n  variant=\"secondary\"\n  leadingComponent={<LandingProductHuntAward />}\n>\n  <Button size=\"xl\" variant=\"secondary\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlineSecondary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryTextCtaSection>;\n```\n\n\n\n### Left-aligned full example\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryTextCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\nimport { LandingSocialProofBand } from '@/components/landing/social-proof/LandingSocialProofBand';\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<LandingSocialProofBand>\n  <LandingSocialProofBandItem>\n    100% encrypted and secure\n  </LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>24/7 customer support</LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>\n    99% customer satisfaction\n  </LandingSocialProofBandItem>\n</LandingSocialProofBand>\n\n<LandingPrimaryTextCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  textPosition=\"left\"\n  withBackground\n  withBackgroundGlow\n  leadingComponent={<LandingProductHuntAward />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">\n      Buy now\n    </a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingSocialProof\n    className=\"w-full mt-12\"\n    showRating\n    numberOfUsers={99}\n    avatarItems={[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Doe',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alice Doe',\n      },\n    ]}\n  />\n</LandingPrimaryTextCtaSection>\n```\n\n\n\n### Centered full example\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryTextCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\nimport { LandingSocialProofBand } from '@/components/landing/social-proof/LandingSocialProofBand';\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<LandingSocialProofBand invert>\n  <LandingSocialProofBandItem>\n    100% encrypted and secure\n  </LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>24/7 customer support</LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>\n    99% customer satisfaction\n  </LandingSocialProofBandItem>\n</LandingSocialProofBand>\n\n<LandingPrimaryTextCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  withBackground\n  withBackgroundGlow\n  variant=\"secondary\"\n  backgroundGlowVariant=\"secondary\"\n  leadingComponent={<LandingProductHuntAward />}\n>\n  <Button size=\"xl\" variant=\"secondary\" asChild>\n    <a href=\"#\">\n      Buy now\n    </a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlineSecondary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full flex justify-center\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n\n  <LandingSocialProof\n    className=\"mt-12 w-full flex justify-center\"\n    showRating\n    numberOfUsers={99}\n    avatarItems={[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Doe',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alice Doe',\n      },\n    ]}\n  />\n</LandingPrimaryTextCtaSection>\n```\n\n\n\n### With Newsletter Form\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryTextCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingNewsletterInput } from '@/components/landing/newsletter/LandingNewsletterInput';\n\n<LandingPrimaryTextCtaSection\n  title=\"Sign up today\"\n  description=\"Never miss an update! Subscribe to our newsletter to get the latest announcements, news and exclusive offers.\"\n>\n  <LandingNewsletterInput />\n</LandingPrimaryTextCtaSection>\n```\n\n\n\n### With Background Effects\n\nMore effects are available in the <a href=\"/boilerplate-documentation/landing-page-components/primary-cta-effects\" className=\"fancy-link\">Primary CTA Background Effects</a> section.\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryTextCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDotParticleCtaBg } from '@/components/landing/cta-backgrounds/LandingDotParticleCtaBg';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\n\n<LandingPrimaryTextCtaSection\n  title=\"Revolutionary Product Launch\"\n  description=\"Experience the future with our cutting-edge solution. Built for modern teams who demand excellence and reliability.\"\n  textPosition=\"center\"\n  effectComponent={<LandingDotParticleCtaBg />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Start Free Trial</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlineSecondary\">\n    <a href=\"#\">Watch Demo</a>\n  </Button>\n\n  <LandingSocialProof\n    className=\"mt-8 w-full flex justify-center\"\n    showRating\n    numberOfUsers={1000}\n    avatarItems={[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Smith',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alex Johnson',\n      },\n    ]}\n  />\n</LandingPrimaryTextCtaSection>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                             | Prop Type                    | Required | Default     |\n| ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -------- | ----------- |\n| **children** <Tippy>React nodes to be rendered within the component (below the text on smaller screen, right side on larger).</Tippy> | `React.ReactNode` ǀ `string` | No       | -           |\n| **className** <Tippy>Additional CSS classes to apply to the component.</Tippy>                                                        | `string`                     | No       | -           |\n| **innerClassName** <Tippy>Additional CSS classes to apply to the inner container of the component.</Tippy>                            | `string`                     | No       | -           |\n| **title** <Tippy>The title text or element to be displayed.</Tippy>                                                                   | `string` ǀ `React.ReactNode` | No       | -           |\n| **titleComponent** <Tippy>Custom React component to be used as the title.</Tippy>                                                     | `React.ReactNode`            | No       | -           |\n| **description** <Tippy>The description text or element to be displayed.</Tippy>                                                       | `string` ǀ `React.ReactNode` | No       | -           |\n| **descriptionComponent** <Tippy>Custom React component to be used as the description.</Tippy>                                         | `React.ReactNode`            | No       | -           |\n| **leadingComponent** <Tippy>Custom React component to be displayed before the title and description.</Tippy>                          | `React.ReactNode`            | No       | -           |\n| **footerComponent** <Tippy>Custom React component to be displayed as the footer of the section.</Tippy>                               | `React.ReactNode`            | No       | -           |\n| **textPosition** <Tippy>The position of the text content within the section. Possible values: `'center'`, `'left'`.</Tippy>           | `'center'` ǀ `'left'`        | No       | `'center'`  |\n| **withBackground** <Tippy>Determines whether to show the section with a background or not.</Tippy>                                    | `boolean`                    | No       | `false`     |\n| **variant** <Tippy>The variant style of the section. Possible values: `'primary'`, `'secondary'`.</Tippy>                             | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n| **effectComponent** <Tippy>Custom React component to be displayed as the background effect.</Tippy>                                    | `React.ReactNode`            | No       | -           |\n| **effectClassName** <Tippy>Additional CSS classes to apply to the background effect.</Tippy>                                        | `string`                     | No       | -           |\n</PropsReference>"
            },
            "PrimaryVideoCta": {
              "description": "A high-impact component designed for landing pages that combines a compelling video with a call-to-action. This section helps draw attention to key messages while using video content to enhance engagement and demonstrate features or products.",
              "api": "<Usage>\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\n```\n\n```jsx\n<LandingPrimaryVideoCtaSection\n  title=\"Your Headline Here\"\n  description=\"Your description text goes here.\"\n  videoSrc=\"path/to/your/video.mp4\"\n  videoPoster=\"path/to/your/poster-image.jpg\"\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Get Started</a>\n  </Button>\n</LandingPrimaryVideoCtaSection>\n```\n</Usage>\n\n<Examples>\n### Centered Text Layout\n\nWhen you need to emphasize both text and video equally, use the centered text layout with the video positioned below.\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\n\n<LandingPrimaryVideoCtaSection\n  title=\"Streamline Your Workflow\"\n  description=\"Our intuitive tools help teams work more efficiently with less effort.\"\n  videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n  textPosition=\"center\"\n  videoPosition=\"center\"\n  withBackground={true}\n  autoPlay={true}\n  loop={true}\n  muted={true}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Try It Free</a>\n  </Button>\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Learn More</a>\n  </Button>\n</LandingPrimaryVideoCtaSection>\n```\n\n\n\n### With Video Controls\n\nDisplay video controls to give users more control over the playback experience.\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\n\n<LandingPrimaryVideoCtaSection\n  title=\"See How It Works\"\n  description=\"Watch our detailed tutorial showing the key features in action.\"\n  videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n  videoPoster=\"/static/images/backdrop-3.webp\"\n  videoPosition=\"right\"\n  controls={true}\n  autoPlay={false}\n  muted={true}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Start Now</a>\n  </Button>\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Contact Sales</a>\n  </Button>\n</LandingPrimaryVideoCtaSection>\n```\n\n\n\n### With Background Glow\n\nAdd a subtle background glow to enhance the visual appeal and draw attention to your content.\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\n\n<LandingPrimaryVideoCtaSection\n  title=\"Powerful Visual Storytelling\"\n  description=\"Engage your audience with stunning visuals and compelling narratives.\"\n  videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n  videoPosition=\"left\"\n  withBackgroundGlow={true}\n  backgroundGlowVariant=\"secondary\"\n  autoPlay={true}\n  loop={true}\n  muted={true}\n>\n  <Button size=\"xl\" variant=\"secondary\" asChild>\n    <a href=\"#\">Get Started</a>\n  </Button>\n  <Button size=\"xl\" variant=\"outlineSecondary\">\n    <a href=\"#\">View Demo</a>\n  </Button>\n</LandingPrimaryVideoCtaSection>\n```\n\n\n\n### With Social Proof\n\nAdd social proof to increase credibility and show popularity of your product.\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\n\n<LandingPrimaryVideoCtaSection\n  title=\"Trusted by Thousands\"\n  description=\"Join the community of professionals leveraging our platform for increased productivity.\"\n  videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n  muted={true}\n  autoPlay={true}\n  loop={true}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Start Free Trial</a>\n  </Button>\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Learn More</a>\n  </Button>\n  <LandingSocialProof\n    className=\"mt-6 w-full\"\n    showRating\n    numberOfUsers={99}\n    avatarItems={[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Doe',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alice Doe',\n      },\n    ]}\n  />\n</LandingPrimaryVideoCtaSection>\n```\n\n\n\n### With Discount/Offer\n\nHighlight special offers or discounts to encourage conversion.\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\n\n<LandingPrimaryVideoCtaSection\n  title=\"Limited Time Offer\"\n  description=\"Get exclusive access to premium features and save with our special pricing.\"\n  videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n  textPosition=\"center\"\n  videoPosition=\"center\"\n  autoPlay={true}\n  loop={true}\n  muted={true}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Claim Discount</a>\n  </Button>\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Learn More</a>\n  </Button>\n  <LandingDiscount\n    className=\"w-full flex justify-center\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryVideoCtaSection>\n```\n\n\n\n### With Discount and Left Alignment\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\n\n<LandingPrimaryVideoCtaSection\n  title=\"Early Bird Pricing\"\n  description=\"Sign up now and save with our early adopter discount program.\"\n  videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n  autoPlay={true}\n  loop={true}\n  muted={true}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Get Started</a>\n  </Button>\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">View Plans</a>\n  </Button>\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingPrimaryVideoCtaSection>\n```\n\n\n\n### With Bullet Points\n\nUse bullet points to highlight key features or benefits of your product.\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingProductFeatureKeyPoints } from '@/components/landing/LandingProductFeatureKeyPoints';\n\n<LandingPrimaryVideoCtaSection\n  title=\"Why Choose Our Platform\"\n  descriptionComponent={\n    <LandingProductFeatureKeyPoints\n      keyPoints={[\n        {\n          title: 'Intelligent Assistance',\n          description:\n            'Receive personalized recommendations and insights tailored to your workflow.',\n        },\n        {\n          title: 'Seamless Collaboration',\n          description:\n            'Easily collaborate with team members and clients in real-time.',\n        },\n        {\n          title: 'Advanced Customization',\n          description:\n            'Tailor your app to fit your unique requirements with extensive customization options.',\n        },\n      ]}\n    />\n  }\n  videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n  autoPlay={true}\n  loop={true}\n  muted={true}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Get Started</a>\n  </Button>\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Learn More</a>\n  </Button>\n</LandingPrimaryVideoCtaSection>\n```\n\n\n\n### With Product Hunt Award\n\nShowcase awards and recognition to build credibility.\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\n\n<LandingPrimaryVideoCtaSection\n  title=\"Award-Winning Platform\"\n  description=\"Join thousands of satisfied users who rely on our platform daily.\"\n  videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n  leadingComponent={<LandingProductHuntAward />}\n  autoPlay={true}\n  loop={true}\n  muted={true}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Get Started</a>\n  </Button>\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Learn More</a>\n  </Button>\n</LandingPrimaryVideoCtaSection>\n```\n\n\n\n### With Social Proof Band\n\nAdd a social proof band to showcase key benefits or testimonials.\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingSocialProofBand } from '@/components/landing/social-proof/LandingSocialProofBand';\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<>\n  <LandingSocialProofBand>\n    <LandingSocialProofBandItem>\n      100% encrypted and secure\n    </LandingSocialProofBandItem>\n    <LandingSocialProofBandItem>24/7 customer support</LandingSocialProofBandItem>\n    <LandingSocialProofBandItem>\n      99% customer satisfaction\n    </LandingSocialProofBandItem>\n  </LandingSocialProofBand>\n  <LandingPrimaryVideoCtaSection\n    title=\"Enterprise-Grade Solution\"\n    description=\"Powerful tools designed for businesses of all sizes, with the security and reliability you need.\"\n    videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n    autoPlay={true}\n    loop={true}\n    muted={true}\n  >\n    <Button size=\"xl\" asChild>\n      <a href=\"#\">Schedule Demo</a>\n    </Button>\n    <Button size=\"xl\" variant=\"outlinePrimary\">\n      <a href=\"#\">Contact Sales</a>\n    </Button>\n  </LandingPrimaryVideoCtaSection>\n</>\n```\n\n\n\n### Full Example with Combined Elements\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\nimport { LandingSocialProofBand } from '@/components/landing/social-proof/LandingSocialProofBand';\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<>\n  <LandingSocialProofBand invert>\n    <LandingSocialProofBandItem>\n      100% encrypted and secure\n    </LandingSocialProofBandItem>\n    <LandingSocialProofBandItem>24/7 customer support</LandingSocialProofBandItem>\n    <LandingSocialProofBandItem>\n      99% customer satisfaction\n    </LandingSocialProofBandItem>\n  </LandingSocialProofBand>\n  <LandingPrimaryVideoCtaSection\n    title=\"Transform Your Business\"\n    description=\"Get everything you need to grow your business with our comprehensive platform.\"\n    videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n    textPosition=\"center\"\n    videoPosition=\"center\"\n    withBackground={true}\n    withBackgroundGlow={true}\n    variant=\"secondary\"\n    leadingComponent={<LandingProductHuntAward />}\n    autoPlay={true}\n    loop={true}\n    muted={true}\n  >\n    <Button size=\"xl\" variant=\"secondary\" asChild>\n      <a href=\"#\">Get Started</a>\n    </Button>\n    <Button size=\"xl\" variant=\"outlineSecondary\">\n      <a href=\"#\">View Pricing</a>\n    </Button>\n    <LandingDiscount\n      className=\"w-full flex justify-center\"\n      discountValueText=\"$350 off\"\n      discountDescriptionText=\"for the first 10 customers (2 left)\"\n    />\n    <LandingSocialProof\n      className=\"mt-12 w-full flex justify-center\"\n      showRating\n      numberOfUsers={99}\n      avatarItems={[\n        {\n          imageSrc: '/static/images/people/1.webp',\n          name: 'John Doe',\n        },\n        {\n          imageSrc: '/static/images/people/2.webp',\n          name: 'Jane Doe',\n        },\n        {\n          imageSrc: '/static/images/people/3.webp',\n          name: 'Alice Doe',\n        },\n      ]}\n    />\n  </LandingPrimaryVideoCtaSection>\n</>\n```\n\n\n\n### With Background Effects\n\nMore effects are available in the <a href=\"/boilerplate-documentation/landing-page-components/primary-cta-effects\" className=\"fancy-link\">Primary CTA Background Effects</a> section.\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDotParticleCtaBg } from '@/components/landing/cta-backgrounds/LandingDotParticleCtaBg';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\n\n<LandingPrimaryVideoCtaSection\n  title=\"Revolutionary Product Launch\"\n  description=\"Experience the future with our cutting-edge solution. Built for modern teams who demand excellence and reliability.\"\n  textPosition=\"center\"\n  videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n  videoPoster=\"/static/images/shipixen/product/1.webp\"\n  videoPosition=\"center\"\n  effectComponent={<LandingDotParticleCtaBg />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Start Free Trial</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlineSecondary\">\n    <a href=\"#\">Watch Demo</a>\n  </Button>\n\n  <LandingSocialProof\n    className=\"mt-8 w-full flex justify-center\"\n    showRating\n    numberOfUsers={1000}\n    avatarItems={[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Smith',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alex Johnson',\n      },\n    ]}\n  />\n</LandingPrimaryVideoCtaSection>\n```\n\n\n\n\n### With Leading Pill\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryVideoCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingLeadingPill } from '@/components/landing/leading/LandingLeadingPill';\n\n<LandingPrimaryVideoCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  imagePosition=\"center\"\n  leadingComponent={<LandingLeadingPill\n    text=\"Best generator\"\n    borderVariant=\"primary\"\n  />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n</LandingPrimaryVideoCtaSection>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **title** <Tippy>The main headline for the CTA section</Tippy> | `string \\| React.ReactNode` | No | - |\n| **titleComponent** <Tippy>Custom component for the title if more control is needed</Tippy> | `React.ReactNode` | No | - |\n| **description** <Tippy>Supporting text that explains the value proposition</Tippy> | `string \\| React.ReactNode` | No | - |\n| **descriptionComponent** <Tippy>Custom component for the description</Tippy> | `React.ReactNode` | No | - |\n| **videoSrc** <Tippy>URL to the video file</Tippy> | `string` | No | - |\n| **videoPoster** <Tippy>URL to the poster image shown before video plays</Tippy> | `string` | No | - |\n| **videoPosition** <Tippy>Position of the video relative to text content</Tippy> | `'left' \\| 'right' \\| 'center'` | No | `'right'` |\n| **videoMaxWidth** <Tippy>Maximum width constraint for the video</Tippy> | `string` | No | `'none'` |\n| **videoShadow** <Tippy>Shadow style for the video container</Tippy> | `'none' \\| 'soft' \\| 'hard'` | No | `'hard'` |\n| **textPosition** <Tippy>Text alignment within the section</Tippy> | `'center' \\| 'left'` | No | `'left'` |\n| **muted** <Tippy>Whether the video should be muted</Tippy> | `boolean` | No | `true` |\n| **autoPlay** <Tippy>Whether the video should auto-play</Tippy> | `boolean` | No | `false` |\n| **controls** <Tippy>Whether to show video controls</Tippy> | `boolean` | No | `false` |\n| **loop** <Tippy>Whether the video should loop</Tippy> | `boolean` | No | `false` |\n| **minHeight** <Tippy>Minimum height of the section</Tippy> | `number` | No | `350` |\n| **withBackground** <Tippy>Whether to show a background color</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Whether to add a glowing background effect</Tippy> | `boolean` | No | `false` |\n| **variant** <Tippy>Color variant for the component</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **backgroundGlowVariant** <Tippy>Color variant for the background glow</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **className** <Tippy>Additional CSS classes for the outer container</Tippy> | `string` | No | - |\n| **innerClassName** <Tippy>Additional CSS classes for the inner container</Tippy> | `string` | No | - |\n| **leadingComponent** <Tippy>Component to display above the title</Tippy> | `React.ReactNode` | No | - |\n| **footerComponent** <Tippy>Component to display at the bottom of the section</Tippy> | `React.ReactNode` | No | - |\n| **children** <Tippy>Call-to-action elements like buttons</Tippy> | `React.ReactNode` | No | - |\n| **effectComponent** <Tippy>Component to display as a background effect</Tippy> | `React.ReactNode` | No | - |\n| **effectClassName** <Tippy>Additional CSS classes for the effect component</Tippy> | `string` | No | - |\n</PropsReference>"
            },
            "ProblemAgitator": {
              "description": "The Problem Agitator component creates a visually engaging layout for highlighting common pain points or problems your users face. It arranges 3-4 problem statements in a grid pattern connected by directional arrows, creating a flow that leads to a cliffhanger or solution teaser.",
              "api": "<Usage>\nImport the components from their respective paths:\n\n```jsx\nimport { LandingProblemAgitator } from '@/components/landing/problem-agitator/LandingProblemAgitator';\nimport { LandingProblemAgitatorItem } from '@/components/landing/problem-agitator/LandingProblemAgitatorItem';\nimport { LandingProblemAgitatorComment } from '@/components/landing/problem-agitator/LandingProblemAgitatorComment';\n```\n\nBasic implementation:\n\n```jsx\n<LandingProblemAgitator\n  title=\"Common User Challenges\"\n  description=\"These are the problems our users face every day\"\n  cliffhanger=\"Our solution makes these problems disappear\"\n>\n  <LandingProblemAgitatorItem>\n    <p>Problem 1</p>\n  </LandingProblemAgitatorItem>\n\n  <LandingProblemAgitatorItem>\n    <p>Problem 2</p>\n    <LandingProblemAgitatorComment className=\"-right-8 top-0\">\n      Pain point 1\n    </LandingProblemAgitatorComment>\n  </LandingProblemAgitatorItem>\n\n  <LandingProblemAgitatorItem>\n    <p>Problem 3</p>\n  </LandingProblemAgitatorItem>\n</LandingProblemAgitator>\n```\n</Usage>\n\n<Examples>\n### Four-Item Grid with Custom Container\n\n\n\n```jsx\n<LandingProblemAgitator\n  titleComponent={\n    <h2 className=\"text-3xl font-bold text-gradient-primary\">\n      Common Website Development Challenges\n    </h2>\n  }\n  containerType=\"narrow\"\n  withBackground={false}\n>\n  <LandingProblemAgitatorItem>\n    <p>Unclear requirements</p>\n  </LandingProblemAgitatorItem>\n\n  <LandingProblemAgitatorItem>\n    <p>Lack of expertise</p>\n  </LandingProblemAgitatorItem>\n\n  <LandingProblemAgitatorItem>\n    <p>Budget constraints</p>\n  </LandingProblemAgitatorItem>\n\n  <LandingProblemAgitatorItem>\n    <p>Time pressure</p>\n  </LandingProblemAgitatorItem>\n</LandingProblemAgitator>\n```\n\n\n\n### Left-aligned Text with Secondary Color Scheme\n\n\n\n```jsx\n<LandingProblemAgitator\n  title=\"Building websites is challenging\"\n  description=\"Traditional approaches leave teams frustrated and projects delayed\"\n  cliffhanger=\"There's a better approach\"\n  textPosition=\"left\"\n  variant=\"secondary\"\n  withBackground\n>\n  <LandingProblemAgitatorItem>\n    <p>Complex requirements</p>\n  </LandingProblemAgitatorItem>\n\n  <LandingProblemAgitatorItem>\n    <p>Limited resources</p>\n    <LandingProblemAgitatorComment className=\"-right-9 top-1 rotate-6\">\n      tight budget\n    </LandingProblemAgitatorComment>\n  </LandingProblemAgitatorItem>\n\n  <LandingProblemAgitatorItem>\n    <p>Technical constraints</p>\n  </LandingProblemAgitatorItem>\n</LandingProblemAgitator>\n```\n\n\n\n### With Background Glow\n\n\n\n```jsx\n<LandingProblemAgitator\n  title=\"Building websites is challenging\"\n  description=\"Traditional approaches leave teams frustrated and projects delayed\"\n  cliffhanger=\"There's a better approach\"\n  withBackgroundGlow\n  withBackground={false}\n>\n  <LandingProblemAgitatorItem>\n    <p>Complex requirements</p>\n  </LandingProblemAgitatorItem>\n\n  <LandingProblemAgitatorItem>\n    <p>Limited resources</p>\n  </LandingProblemAgitatorItem>\n\n  <LandingProblemAgitatorItem>\n    <p>Technical constraints</p>\n  </LandingProblemAgitatorItem>\n</LandingProblemAgitator>\n```\n\n\n</Examples>\n\n<PropsReference>\n### LandingProblemAgitator\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **className** <Tippy>Additional CSS classes to apply to the section</Tippy> | `string` | No | `-` |\n| **children** <Tippy>LandingProblemAgitatorItem components (3 or 4 recommended)</Tippy> | `React.ReactNode` | Yes | - |\n| **title** <Tippy>Main heading text for the section</Tippy> | `string \\| React.ReactNode` | No | `-` |\n| **titleComponent** <Tippy>Custom React component for the title (used instead of title prop)</Tippy> | `React.ReactNode` | No | `-` |\n| **description** <Tippy>Descriptive text displayed below the title</Tippy> | `string \\| React.ReactNode` | No | `-` |\n| **descriptionComponent** <Tippy>Custom React component for the description (used instead of description prop)</Tippy> | `React.ReactNode` | No | `-` |\n| **cliffhanger** <Tippy>Conclusion text displayed after the problem items</Tippy> | `string \\| React.ReactNode` | No | `-` |\n| **cliffhangerComponent** <Tippy>Custom React component for the cliffhanger (used instead of cliffhanger prop)</Tippy> | `React.ReactNode` | No | `-` |\n| **textPosition** <Tippy>Alignment of text within the component</Tippy> | `'center' \\| 'left'` | No | `'center'` |\n| **withBackground** <Tippy>Whether to display a background color</Tippy> | `boolean` | No | `true` |\n| **withBackgroundGlow** <Tippy>Whether to include a glow effect in the background</Tippy> | `boolean` | No | `false` |\n| **variant** <Tippy>Color scheme variant</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **backgroundGlowVariant** <Tippy>Color scheme for the background glow effect</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **containerType** <Tippy>Width constraint for the container</Tippy> | `'narrow' \\| 'wide' \\| 'ultrawide'` | No | `'ultrawide'` |\n\n### LandingProblemAgitatorItem\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **className** <Tippy>Additional CSS classes to apply to the item</Tippy> | `string` | No | `-` |\n| **children** <Tippy>Content of the problem item (text and optional LandingProblemAgitatorComment components)</Tippy> | `React.ReactNode` | Yes | - |\n\n### LandingProblemAgitatorComment\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **className** <Tippy>Additional CSS classes to apply to the comment (use for positioning)</Tippy> | `string` | No | `-` |\n| **children** <Tippy>Text content of the comment</Tippy> | `React.ReactNode` | Yes | - |\n</PropsReference>"
            },
            "ProblemSolution": {
              "description": "The Problem / Solution component presents a clear comparison between problems and their solutions. This pattern is effective for highlighting how your product addresses specific pain points and provides value to users.",
              "api": "<Usage>\nImport the component:\n\n```jsx\nimport { LandingProductProblemSolution } from '@/components/landing/LandingProductProblemSolution';\n```\n\nBasic implementation:\n\n```jsx\n<LandingProductProblemSolution\n  title=\"Problems and Solutions\"\n  description=\"Addressing key customer pain points with innovative solutions\"\n  problems={[\n    { title: 'Problem title' },\n  ]}\n  solutions={[\n    { title: 'Solution title' },\n  ]}\n/>\n```\n</Usage>\n\n<Examples>\n### No titles\n\n\n\n```jsx\nimport { LandingProductProblemSolution } from '@/components/landing/LandingProductProblemSolution';\n<LandingProductProblemSolution\n  solutionTitle=''\n  problemTitle=''\n  problems={[\n    { title: \"Too many steps\" },\n    { title: \"Too many clicks\" },\n  ]}\n  solutions={[\n    { title: \"One-click installation\" },\n    { title: \"Optimized performance\" },\n  ]}\n/>\n```\n\n\n\n### With Left Alignment\n\n\n\n```jsx\nimport { LandingProductProblemSolution } from '@/components/landing/LandingProductProblemSolution';\n\n<LandingProductProblemSolution\n  title=\"Problems and Solutions\"\n  description=\"Addressing key customer pain points with innovative solutions\"\n  textPosition=\"left\"\n  problems={[\n    { title: 'Complex setup process' },\n    { title: 'Poor performance' }\n  ]}\n  solutions={[\n    { title: 'One-click installation' },\n    { title: 'Optimized performance' }\n  ]}\n/>\n```\n\n\n\n### Custom Titles\n\n\n\n```jsx\nimport { LandingProductProblemSolution } from '@/components/landing/LandingProductProblemSolution';\n\n<LandingProductProblemSolution\n  title=\"Before vs After\"\n  description=\"See the difference our product makes\"\n  problemTitle=\"Before\"\n  solutionTitle=\"After\"\n  problems={[\n    { title: 'Manual process' },\n  ]}\n  solutions={[\n    { title: 'Automated workflow' },\n  ]}\n/>\n```\n\n\n\n### With descriptions\n\n\n\n```jsx\nimport { LandingProductProblemSolution } from '@/components/landing/LandingProductProblemSolution';\n\n<LandingProductProblemSolution\n  title=\"Before vs After\"\n  description=\"See the difference our product makes\"\n  problemTitle=\"Before\"\n  solutionTitle=\"After\"\n  problems={[\n    { title: 'Manual process', description: 'Time-consuming and error-prone' },\n    { title: 'Manual process', description: 'Time-consuming and error-prone' },\n  ]}\n  solutions={[\n    { title: 'Automated workflow', description: 'Fast, accurate, and reliable' },\n    { title: 'Automated workflow', description: 'Fast, accurate, and reliable' },\n  ]}\n/>\n\n```\n\n\n\n### With Background\n\n\n\n```jsx\nimport { LandingProductProblemSolution } from '@/components/landing/LandingProductProblemSolution';\n\n<LandingProductProblemSolution\n  title=\"Problems and Solutions\"\n  description=\"Addressing key customer pain points with innovative solutions\"\n  withBackground={true}\n  problems={[\n    { title: 'Complex setup process' },\n    { title: 'Poor performance' }\n  ]}\n  solutions={[\n    { title: 'One-click installation' },\n    { title: 'Optimized performance' }\n  ]}\n/>\n```\n\n\n\n### With Background Glow\n\n\n\n```jsx\nimport { LandingProductProblemSolution } from '@/components/landing/LandingProductProblemSolution';\n\n<LandingProductProblemSolution\n  title=\"Problems and Solutions\"\n  withBackgroundGlow={true}\n  problems={[\n    { title: 'Complex setup process' },\n    { title: 'Poor performance' }\n  ]}\n  solutions={[\n    { title: 'One-click installation' },\n    { title: 'Optimized performance' }\n  ]}\n/>\n```\n\n\n</Examples>\n\n<PropsReference>\n### LandingProductProblemSolution Props\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **className** <Tippy>Additional CSS classes to apply to the section container</Tippy> | `string` | No | - |\n| **problems** <Tippy>Array of problems to display</Tippy> | `KeyPoint[]` | Yes | - |\n| **solutions** <Tippy>Array of solutions to display</Tippy> | `KeyPoint[]` | Yes | - |\n| **title** <Tippy>Main heading text for the section</Tippy> | `string` | No | - |\n| **titleComponent** <Tippy>Custom React component to replace the default title</Tippy> | `ReactNode` | No | - |\n| **description** <Tippy>Supporting text that appears under the title</Tippy> | `string` | No | - |\n| **descriptionComponent** <Tippy>Custom React component to replace the default description</Tippy> | `ReactNode` | No | - |\n| **solutionTitle** <Tippy>Heading text for the solution column</Tippy> | `string` | No | `'Solution'` |\n| **solutionTitleComponent** <Tippy>Custom React component to replace the default solution title</Tippy> | `ReactNode` | No | - |\n| **problemTitle** <Tippy>Heading text for the problem column</Tippy> | `string` | No | `'Problem'` |\n| **problemTitleComponent** <Tippy>Custom React component to replace the default problem title</Tippy> | `ReactNode` | No | - |\n| **variant** <Tippy>Color theme variant</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **withBackground** <Tippy>Apply a subtle background color based on the variant</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Add a background glow effect</Tippy> | `boolean` | No | `false` |\n| **backgroundGlowVariant** <Tippy>Color theme for the background glow</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **textPosition** <Tippy>Alignment of the section title and description</Tippy> | `'center' \\| 'left'` | No | `'center'` |\n\n### Interfaces\n\n```ts\nexport interface KeyPoint {\n  title: string;\n  description?: string;\n}\n```\n\nThe `KeyPoint` interface is used both for problems and solutions to provide consistent formatting.\n</PropsReference>"
            },
            "ProductCard": {
              "description": "The Product Card Section component is designed to showcase products in a responsive grid layout with customizable columns. It's perfect for e-commerce sections, featured products, or any collection of items that need to be displayed in an organized grid format. The component supports both programmatic usage with an array of products and declarative usage with children components.",
              "api": "<Usage>\n```jsx\nimport { LandingProductCardSection } from '@/components/landing/card/LandingProductCardSection';\nimport { LandingProductCard } from '@/components/landing/card/LandingProductCard';\n```\n\n```jsx\n<LandingProductCardSection\n  title=\"Our Products\"\n  description=\"Explore our collection\"\n  products={[\n    {\n      title: \"Product 1\",\n      description: \"Description of product 1\",\n      imageSrc: \"/static/images/backdrop-1.webp\",\n    },\n    // ... more products\n  ]}\n/>\n```\n</Usage>\n\n<Examples>\n### Four Column Grid\n\nDisplay products in a 4-column grid for a more compact layout.\n\n\n\n```jsx\nimport { LandingProductCardSection } from '@/components/landing/card/LandingProductCardSection';\nimport { Button } from '@/components/shared/ui/button';\n\n<LandingProductCardSection\n  title=\"Featured Collection\"\n  description=\"A selection of our most popular items\"\n  gridColumns={4}\n  variant=\"secondary\"\n  withBackground\n  textPosition=\"left\"\n  products={[\n    {\n      title: \"Side Table\",\n      description: \"Compact side table with marble top.\",\n      imageSrc: \"/static/images/backdrop-12.webp\",\n      actionComponent: <Button>View</Button>\n    },\n    {\n      title: \"Floor Lamp\",\n      description: \"Adjustable floor lamp with linen shade.\",\n      imageSrc: \"/static/images/backdrop-13.webp\",\n      actionComponent: <Button>Details</Button>\n    },\n    {\n      title: \"Bookshelf\",\n      description: \"Modular bookshelf with adjustable shelves.\",\n      imageSrc: \"/static/images/backdrop-14.webp\",\n      actionComponent: <Button>View</Button>\n    },\n    {\n      title: \"Desk Chair\",\n      description: \"Ergonomic desk chair with breathable mesh.\",\n      imageSrc: \"/static/images/backdrop-15.webp\",\n      actionComponent: <Button>Details</Button>\n    }\n  ]}\n/>\n```\n\n\n\n\n### Component-Based Usage\n\nYou can use the component-based approach by providing child `LandingProductCard` components directly. This allows for more customization and flexibility.\n\n\n\n```jsx\nimport { LandingProductCardSection } from '@/components/landing/card/LandingProductCardSection';\nimport { LandingProductCard } from '@/components/landing/card/LandingProductCard';\nimport { Button } from '@/components/shared/ui/button';\n\n<LandingProductCardSection\n  title=\"Product Collection\"\n  description=\"Handcrafted with attention to detail\"\n>\n  <LandingProductCard\n    title=\"Wooden Table\"\n    description=\"Handcrafted oak dining table with natural finish.\"\n    imageSrc=\"/static/images/backdrop-6.webp\"\n    actionComponent={<Button>Add to Cart</Button>}\n  />\n  <LandingProductCard\n    title=\"Ceramic Vase\"\n    description=\"Hand-thrown ceramic vase with unique glaze pattern.\"\n    imageSrc=\"/static/images/backdrop-7.webp\"\n    actionComponent={<Button variant=\"secondary\">View Details</Button>}\n    variant=\"secondary\"\n    featured\n  />\n  <LandingProductCard\n    title=\"Wall Sconce\"\n    description=\"Modern wall sconce with adjustable arm and warm lighting.\"\n    imageSrc=\"/static/images/backdrop-8.webp\",\n    actionComponent={<Button>Purchase</Button>}\n  />\n</LandingProductCardSection>\n```\n\n\n\n### With Background\n\n\n\n```jsx\nimport { LandingProductCardSection } from '@/components/landing/card/LandingProductCardSection';\nimport { Button } from '@/components/shared/ui/button';\n\n<LandingProductCardSection\n  title=\"Premium Collection\"\n  description=\"Our finest selection of premium products\"\n  withBackground\n  variant=\"primary\"\n  gridColumns={3}\n  products={[\n    {\n      title: \"Leather Armchair\",\n      description: \"Premium leather armchair with walnut frame.\",\n      imageSrc: \"/static/images/backdrop-9.webp\",\n      actionComponent: <Button>View Details</Button>\n    },\n    {\n      title: \"Pendant Light\",\n      description: \"Handblown glass pendant light with brass accents.\",\n      imageSrc: \"/static/images/backdrop-10.webp\",\n      actionComponent: <Button>Purchase</Button>\n    },\n    {\n      title: \"Area Rug\",\n      description: \"Hand-knotted wool area rug in natural colors.\",\n      imageSrc: \"/static/images/backdrop-11.webp\",\n      actionComponent: <Button>View Details</Button>\n    }\n  ]}\n/>\n```\n\n\n\n\n### Custom Card Components\n\nDemonstrate using the `topComponent` and `bottomComponent` props to add custom content to the product cards.\n\n\n\n```jsx\nimport { LandingProductCardSection } from '@/components/landing/card/LandingProductCardSection';\nimport { LandingProductCard } from '@/components/landing/card/LandingProductCard';\nimport { Button } from '@/components/shared/ui/button';\nimport { Badge } from '@/components/shared/ui/badge';\n\n<LandingProductCardSection\n  title=\"Custom Product Cards\"\n  description=\"Featuring custom component slots for flexible layouts\"\n>\n  <LandingProductCard\n    title=\"Premium Chair\"\n    description=\"Ergonomic office chair with adjustable height and lumbar support.\"\n    imageSrc=\"/static/images/backdrop-16.webp\"\n    topComponent={<Badge>New Arrival</Badge>}\n    actionComponent={<Button className=\"w-full\">Add to Cart</Button>}\n  />\n  <LandingProductCard\n    title=\"Standing Desk\"\n    description=\"Height-adjustable standing desk with memory presets.\"\n    imageSrc=\"/static/images/backdrop-17.webp\"\n    topComponent={<Badge variant=\"destructive\">Bestseller</Badge>}\n    bottomComponent={<div className=\"flex justify-center\"><LandingRating /></div>}\n    actionComponent={<Button className=\"w-full\">View Details</Button>}\n  />\n  <LandingProductCard\n    title=\"Desk Lamp\"\n    description=\"Adjustable desk lamp with wireless charging base.\"\n    imageSrc=\"/static/images/backdrop-18.webp\"\n    topComponent={<Badge variant=\"secondary\">Limited Edition</Badge>}\n    bottomComponent={<div className=\"text-sm font-medium text-green-600\">In stock - ships in 24 hours</div>}\n    actionComponent={<Button className=\"w-full\">Buy Now</Button>}\n  />\n</LandingProductCardSection>\n```\n\n\n</Examples>\n\n<PropsReference>\n### LandingProductCardSection Props\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **title** <Tippy>Main title of the product card section</Tippy> | `string` | No | - |\n| **titleComponent** <Tippy>Custom component for the title</Tippy> | `React.ReactNode` | No | - |\n| **description** <Tippy>Text description displayed below the title</Tippy> | `string` | No | - |\n| **descriptionComponent** <Tippy>Custom component for the description</Tippy> | `React.ReactNode` | No | - |\n| **products** <Tippy>Array of product objects to be displayed</Tippy> | `ProductCardProps[]` | No | `[]` |\n| **children** <Tippy>Child components to render instead of products array</Tippy> | `React.ReactNode` | No | - |\n| **className** <Tippy>Additional CSS classes for the section</Tippy> | `string` | No | `''` |\n| **innerClassName** <Tippy>Additional CSS classes for the inner container</Tippy> | `string` | No | `''` |\n| **gridClassName** <Tippy>Additional CSS classes for the grid</Tippy> | `string` | No | `''` |\n| **gridColumns** <Tippy>Number of columns to display on desktop</Tippy> | `3 \\| 4` | No | `3` |\n| **withBackground** <Tippy>Whether to display a background color</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Whether to display a background glow effect</Tippy> | `boolean` | No | `false` |\n| **backgroundGlowVariant** <Tippy>Color variant for the background glow</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **variant** <Tippy>Color variant for the section</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **textPosition** <Tippy>Alignment of the text content</Tippy> | `'center' \\| 'left'` | No | `'center'` |\n\n### LandingProductCard Props\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **title** <Tippy>Title of the product</Tippy> | `string` | Yes | - |\n| **titleComponent** <Tippy>Custom component for the title</Tippy> | `React.ReactNode` | No | - |\n| **description** <Tippy>Description of the product</Tippy> | `string` | No | - |\n| **descriptionComponent** <Tippy>Custom component for the description</Tippy> | `React.ReactNode` | No | - |\n| **imageSrc** <Tippy>Source URL for the product image</Tippy> | `string` | No | - |\n| **imageAlt** <Tippy>Alt text for the product image</Tippy> | `string` | No | `''` |\n| **actionComponent** <Tippy>Component for the call-to-action (e.g., button)</Tippy> | `React.ReactNode` | No | - |\n| **topComponent** <Tippy>Custom component to display at the top of the card content</Tippy> | `React.ReactNode` | No | - |\n| **bottomComponent** <Tippy>Custom component to display at the bottom of the card content</Tippy> | `React.ReactNode` | No | - |\n| **featured** <Tippy>Whether to highlight this card as featured</Tippy> | `boolean` | No | `false` |\n| **className** <Tippy>Additional CSS classes for the card</Tippy> | `string` | No | - |\n| **variant** <Tippy>Color variant for the card</Tippy> | `'primary' \\| 'secondary' \\| 'default'` | No | `'default'` |\n| **href** <Tippy>URL to navigate to when the card is clicked</Tippy> | `string` | No | - |\n\n### Type Definitions\n\n```typescript\nexport interface ProductCardProps {\n  title: string;\n  titleComponent?: React.ReactNode;\n  description?: string;\n  descriptionComponent?: React.ReactNode;\n  imageSrc?: string;\n  imageAlt?: string;\n  actionComponent?: React.ReactNode;\n  topComponent?: React.ReactNode;\n  bottomComponent?: React.ReactNode;\n  featured?: boolean;\n  className?: string;\n  variant?: 'primary' | 'secondary' | 'default';\n  href?: string;\n}\n```\n\n```typescript\nexport interface LandingProductCardSectionProps {\n  title?: string;\n  titleComponent?: React.ReactNode;\n  description?: string;\n  descriptionComponent?: React.ReactNode;\n  products?: ProductCardProps[];\n  children?: React.ReactNode;\n  className?: string;\n  innerClassName?: string;\n  gridClassName?: string;\n  gridColumns?: 3 | 4;\n  withBackground?: boolean;\n  withBackgroundGlow?: boolean;\n  backgroundGlowVariant?: 'primary' | 'secondary';\n  variant?: 'primary' | 'secondary';\n  textPosition?: 'center' | 'left';\n}\n```\n</PropsReference>"
            },
            "ProductFeature": {
              "description": "This component can display a product feature e.g. on your landing page, features page or elsewhere. It can show an image on the left, right or center; either in perspective or flat and has many customization options. Use this to highlight a feature or key aspect of your product with text and an optional image.",
              "api": "<Usage>\n```jsx\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n```\n\n```jsx\n<LandingProductFeature\n  title=\"The wait is over\"\n  description=\"Give your project the home it deserves. Your users will love you for it.\"\n  imageSrc=\"/static/images/backdrop-5.webp\"\n  imageAlt=\"Sample image\"\n/>\n```\n</Usage>\n\n<Examples>\n### Image position\n\n\n\n```jsx\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductFeature\n  imagePosition=\"left\"\n  title=\"The wait is over\"\n  description=\"Give your project the home it deserves. Your users will love you for it.\"\n  imageSrc=\"/static/images/backdrop-5.webp\"\n  imageAlt=\"Sample image\"\n/>;\n```\n\n\n\n### Image perspective\n\n\n\n```jsx\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductFeature\n  imagePerspective=\"paper\"\n  title=\"The wait is over\"\n  description=\"Give your project the home it deserves. Your users will love you for it.\"\n  imageSrc=\"/static/images/backdrop-5.webp\"\n  imageAlt=\"Sample image\"\n/>;\n```\n\n\n\n### Customization\n\nIt is also possible to customize the background color, change text\nposition or disable zooming on hover.\n\n\n\n```jsx\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductFeature\n  withBackground\n  variant=\"secondary\"\n  zoomOnHover={false}\n  imagePosition=\"left\"\n  imagePerspective=\"right\"\n  title=\"The wait is over\"\n  description=\"Give your project the home it deserves. Your users will love you for it.\"\n  imageSrc=\"/static/images/backdrop-5.webp\"\n  imageAlt=\"Sample image\"\n/>;\n```\n\n\n\n### With Background Glow\n\n\n\n```jsx\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductFeature\n  withBackgroundGlow\n  backgroundGlowVariant=\"primary\"\n  title=\"The wait is over\"\n  description=\"Give your project the home it deserves. Your users will love you for it.\"\n  imageSrc=\"/static/images/backdrop-5.webp\"\n  imageAlt=\"Sample image\"\n/>;\n```\n\n\n\n### With Bullet Points\n\n\n\n```jsx\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\nimport { LandingProductFeatureKeyPoints } from '@/components/landing/LandingProductFeatureKeyPoints';\n\n<LandingProductFeature\n  title=\"The wait is over\"\n  descriptionComponent={\n    <>\n      <LandingProductFeatureKeyPoints\n        keyPoints={[\n          {\n            title: 'Intelligent Assistance',\n            description:\n              'Receive personalized recommendations and insights tailored to your workflow.',\n          },\n          {\n            title: 'Seamless Collaboration',\n            description:\n              'Easily collaborate with team members and clients in real-time.',\n          },\n          {\n            title: 'Advanced Customization',\n            description:\n              'Tailor your app to fit your unique requirements with extensive customization options.',\n          },\n        ]}\n      />\n    </>\n  }\n  imageSrc=\"/static/images/backdrop-5.webp\"\n  imageAlt=\"Sample image\"\n/>;\n```\n\n\n\n### With Call to Action (CTA)\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductFeature\n  title=\"The wait is over\"\n  descriptionComponent={\n    <>\n      <p>\n        Receive personalized recommendations and insights tailored to your\n        workflow.\n      </p>\n\n      <Button className=\"mt-8\" asChild>\n        <a href=\"#\">Try now for free</a>\n      </Button>\n\n      <p className=\"text-sm\">7 day free trial, no credit card required.</p>\n    </>\n  }\n  imageSrc=\"/static/images/backdrop-5.webp\"\n  imageAlt=\"Sample image\"\n/>;\n```\n\n\n\n### With Features Grid\n\n\n\n```jsx\nimport { LandingProductFeaturesGrid } from '@/components/landing/LandingProductFeaturesGrid';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductFeaturesGrid\n  title=\"Get the job done in no time\"\n  description=\"You'll save days of work and the only question you'll have is 'What do I do with all this free time?'\"\n>\n  <LandingProductFeature\n    title=\"Deploy\"\n    description=\"Give your project the home it deserves.\"\n    imageSrc=\"/static/images/shipixen/product/14.webp\"\n    imageAlt=\"Sample image\"\n  />\n\n  <LandingProductFeature\n    title=\"No config\"\n    description=\"No configuration needed. We take care of it.\"\n    imageSrc=\"/static/images/shipixen/product/4.webp\"\n    imageAlt=\"Sample image\"\n  />\n\n  <LandingProductFeature\n    title=\"Theme\"\n    description=\"Choose from more than 30+ themes or create your own.\"\n    imageSrc=\"/static/images/shipixen/product/2.webp\"\n    imageAlt=\"Sample image\"\n  />\n</LandingProductFeaturesGrid>;\n```\n\n\n\n### With Product Steps\n\n\n\n```jsx\nimport { LandingProductSteps } from '@/components/landing/LandingProductSteps';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductSteps\n  title=\"How it works\"\n  description=\"Follow these simple steps to get started with our product.\"\n>\n  <LandingProductFeature\n    title=\"Create your account\"\n    description=\"Sign up in seconds and get instant access to our platform.\"\n    imageSrc=\"/static/images/backdrop-4.webp\"\n  />\n  <LandingProductFeature\n    title=\"Customize your workflow\"\n    description=\"Set up your preferences and configure your workspace.\"\n    imageSrc=\"/static/images/backdrop-8.webp\"\n  />\n  <LandingProductFeature\n    title=\"Start collaborating\"\n    description=\"Invite your team and begin working together seamlessly.\"\n    imageSrc=\"/static/images/backdrop-9.webp\"\n  />\n</LandingProductSteps>\n```\n\n\n\n### With Background Effect\n\n\n\n```jsx\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\nimport { LandingDotParticleCtaBg } from '@/components/landing/cta-backgrounds/LandingDotParticleCtaBg';\n\n<LandingProductFeature\n  title=\"The wait is over\"\n  description=\"Give your project the home it deserves. Your users will love you for it.\"\n  imageSrc=\"/static/images/backdrop-5.webp\"\n  imageAlt=\"Sample image\"\n  effectComponent={<LandingDotParticleCtaBg />}\n/>;\n```\n\n\n\n\n\n### With Leading Pill\n\n\n\n```jsx\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductFeature\n  title=\"The wait is over\"\n  description=\"Give your project the home it deserves. Your users will love you for it.\"\n  imageSrc=\"/static/images/backdrop-5.webp\"\n  imageAlt=\"Sample image\"\n  leadingComponent={\n    <LandingLeadingPill\n      withBorder={false}\n      withBackground={true}\n      backgroundVariant=\"primary\"\n      leftComponent={<SparklesIcon className=\"w-4 h-4\" />}\n    >\n      Join today\n    </LandingLeadingPill>\n  }\n/>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                              | Prop Type                                                                | Required | Default     |\n| -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | -------- | ----------- |\n| **children** <Tippy>React nodes to be rendered within the component (below the text on smaller screen, right side on larger).</Tippy>  | `React.ReactNode` ǀ `string`                                             | No       | -           |\n| **className** <Tippy>Additional CSS classes to apply to the component.</Tippy>                                                         | `string`                                                                 | No       | -           |\n| **innerClassName** <Tippy>Additional CSS classes to apply to the inner container of the component.</Tippy>                             | `string`                                                                 | No       | -           |\n| **title** <Tippy>The title to be displayed in the component.</Tippy>                                                                   | `string` ǀ `React.ReactNode`                                             | No       | -           |\n| **titleComponent** <Tippy>Custom React component to render as the title.</Tippy>                                                       | `React.ReactNode`                                                        | No       | -           |\n| **description** <Tippy>The description to be displayed in the component.</Tippy>                                                       | `string` ǀ `React.ReactNode`                                             | No       | -           |\n| **descriptionComponent** <Tippy>Custom React component to render as the description.</Tippy>                                           | `React.ReactNode`                                                        | No       | -           |\n| **leadingComponent** <Tippy>Custom React component to render as the leading component.</Tippy>                                         | `React.ReactNode`                                                        | No       | -           |\n| **textPosition** <Tippy>The position of the text content ('center' or 'left').</Tippy>                                                 | `'center'` ǀ `'left'`                                                    | No       | `'left'`    |\n| **imageSrc** <Tippy>The URL of the image to be displayed.</Tippy>                                                                      | `string`                                                                 | No       | -           |\n| **imageAlt** <Tippy>The alternative text for the image.</Tippy>                                                                        | `string`                                                                 | No       | ''          |\n| **imagePosition** <Tippy>The position of the image ('left', 'right', or 'center').</Tippy>                                             | `'left'` ǀ `'right'` ǀ `'center'`                                        | No       | `'right'`   |\n| **imagePerspective** <Tippy>The perspective effect for the image ('none', 'left', 'right', 'bottom', 'bottom-lg', or 'paper').</Tippy> | `'none'` ǀ `'left'` ǀ `'right'` ǀ `'bottom'` ǀ `'bottom-lg'` ǀ `'paper'` | No       | `'paper'`   |\n| **imageShadow** <Tippy>The shadow effect for the image ('none', 'soft', or 'hard').</Tippy>                                            | `'none'` ǀ `'soft'` ǀ `'hard'`                                           | No       | `'hard'`    |\n| **zoomOnHover** <Tippy>Whether to enable zoom effect on hover for the image.</Tippy>                                                   | `boolean`                                                                | No       | `true`      |\n| **minHeight** <Tippy>The minimum height of the component.</Tippy>                                                                      | `number`                                                                 | No       | `350`       |\n| **withBackground** <Tippy>Whether to include a background for the component.</Tippy>                                                   | `boolean`                                                                | No       | `false`     |\n| **withBackgroundGlow** <Tippy>Whether to include a glowing background effect.</Tippy>                                                  | `boolean`                                                                | No       | `false`     |\n| **variant** <Tippy>The variant of the component ('primary' or 'secondary').</Tippy>                                                    | `'primary'` ǀ `'secondary'`                                              | No       | `'primary'` |\n| **backgroundGlowVariant** <Tippy>The variant of the glowing background effect ('primary' or 'secondary').</Tippy>                      | `'primary'` ǀ `'secondary'`                                              | No       | `'primary'` |\n| **effectComponent** <Tippy>Custom React component to be displayed as the background effect.</Tippy>                                    | `React.ReactNode`                                                        | No       | -           |\n| **effectClassName** <Tippy>Additional CSS classes to apply to the background effect.</Tippy>                                        | `string`                                                                 | No       | -           |\n</PropsReference>"
            },
            "ProductFeaturesGrid": {
              "description": "This component displays a title, description and a grid of [Product Feature](/boilerplate-documentation/landing-page-components/product-feature) and/or [Product Video Feature](/boilerplate-documentation/landing-page-components/product-video-feature) (in any combination, passed as children). The component is responsive and will display a different number of columns depending on the number of features passed. The number of columns can be specified using the `numberOfColumns` prop.",
              "api": "<Usage>\n```jsx\nimport { LandingProductFeaturesGrid } from '@/components/landing/LandingProductFeaturesGrid';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n```\n\n```jsx\n<LandingProductFeaturesGrid\n  title=\"Get the job done in no time\"\n  description=\"You'll save days of work and the only question you'll have is 'What do I do with all this free time?'\"\n  numberOfColumns={2}\n>\n  <LandingProductFeature\n    title=\"The wait is over\"\n    description=\"Give your project the home it deserves. Your users will love you for it.\"\n    imageSrc=\"/static/images/shipixen/product/14.webp\"\n    imageAlt=\"Sample image\"\n  />\n\n  <LandingProductFeature\n    title=\"Branding\"\n    description=\"No configuration needed. We take care of everything for you, just press a button.\"\n    imageSrc=\"/static/images/shipixen/product/4.webp\"\n    imageAlt=\"Sample image\"\n  />\n\n  <LandingProductFeature\n    title=\"39+ themes\"\n    description=\"Choose from more than 30+ themes or create your own. Upload your logo, set the size and we take care of the rest.\"\n    imageSrc=\"/static/images/shipixen/product/2.webp\"\n    imageAlt=\"Sample image\"\n  />\n</LandingProductFeaturesGrid>\n```\n</Usage>\n\n<Examples>\n### Usage with video features\n\n\n\n```jsx\nimport { LandingProductFeaturesGrid } from '@/components/landing/LandingProductFeaturesGrid';\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\n\n<LandingProductFeaturesGrid\n  title=\"Get the job done in no time\"\n  description=\"You'll save days of work and the only question you'll have is 'What do I do with all this free time?'\"\n  numberOfColumns={2}\n>\n  <LandingProductVideoFeature\n    title=\"Generate\"\n    description=\"Save time by generating features, sales copy, FAQs and even example testimonials with AI. All beautifully designed.\"\n    autoPlay={false}\n    videoSrc=\"https://cache.shipixen.com/features/2-generate-content-with-ai.mp4\"\n  />\n\n  <LandingProductVideoFeature\n    title=\"Design\"\n    description=\"Choose from more than 30+ themes or create your own. Upload your logo, set the size and we take care of the rest.\"\n    autoPlay={false}\n    videoSrc=\"https://cache.shipixen.com/features/3-theme-and-logo.mp4\"\n  />\n\n  <LandingProductVideoFeature\n    title=\"Build\"\n    description=\"Use our pricing page builder to create a beautiful pricing page. Choose from different layouts and monthly/yearly pricing options. It's as easy as it looks.\"\n    autoPlay={false}\n    videoSrc=\"https://cache.shipixen.com/features/11-pricing-page-builder.mp4\"\n  />\n</LandingProductFeaturesGrid>;\n```\n\n\n\n### Mixing features\n\n\n\n```jsx\nimport { LandingProductFeaturesGrid } from '@/components/landing/LandingProductFeaturesGrid';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\n\n<LandingProductFeaturesGrid\n  title=\"Get the job done in no time\"\n  description=\"You'll save days of work and the only question you'll have is 'What do I do with all this free time?'\"\n>\n  <LandingProductFeature\n    title=\"Design\"\n    description=\"No configuration needed. We take care of everything for you, just press a button.\"\n    imageSrc=\"/static/images/shipixen/product/4.webp\"\n    imageAlt=\"Sample image\"\n  />\n\n  <LandingProductVideoFeature\n    title=\"Build\"\n    description=\"Use our pricing page builder to create a beautiful pricing page. Choose from different layouts and monthly/yearly pricing options. It's as easy as it looks.\"\n    autoPlay={true}\n    videoSrc=\"https://cache.shipixen.com/features/11-pricing-page-builder.mp4\"\n  />\n</LandingProductFeaturesGrid>;\n```\n\n\n\n### Customization\n\n\n\n```jsx\nimport { LandingProductFeaturesGrid } from '@/components/landing/LandingProductFeaturesGrid';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\n\n<LandingProductFeaturesGrid\n  title=\"Get the job done in no time\"\n  description=\"You'll save days of work and the only question you'll have is 'What do I do with all this free time?'\"\n>\n  <LandingProductFeature\n    title=\"Design\"\n    description=\"No configuration needed. We take care of everything for you, just press a button.\"\n    imageSrc=\"/static/images/shipixen/product/4.webp\"\n    imageAlt=\"Sample image\"\n  />\n\n  <LandingProductVideoFeature\n    title=\"Build\"\n    description=\"Use our pricing page builder to create a beautiful pricing page. Choose from different layouts and monthly/yearly pricing options. It's as easy as it looks.\"\n    autoPlay={true}\n    videoSrc=\"https://cache.shipixen.com/features/11-pricing-page-builder.mp4\"\n  />\n</LandingProductFeaturesGrid>;\n```\n\n\n\n### With a 3 column layout\n\n\n\n```jsx\nimport { LandingProductFeaturesGrid } from '@/components/landing/LandingProductFeaturesGrid';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductFeaturesGrid\n  title=\"Get the job done in no time\"\n  description=\"You'll save days of work and the only question you'll have is 'What do I do with all this free time?'\"\n  numberOfColumns={3}\n>\n  <LandingProductFeature\n    title=\"Deploy\"\n    description=\"Give your project the home it deserves.\"\n    imageSrc=\"/static/images/shipixen/product/14.webp\"\n    imageAlt=\"Sample image\"\n  />\n\n  <LandingProductFeature\n    title=\"No config\"\n    description=\"No configuration needed. We take care of it.\"\n    imageSrc=\"/static/images/shipixen/product/4.webp\"\n    imageAlt=\"Sample image\"\n  />\n\n  <LandingProductFeature\n    title=\"Theme\"\n    description=\"Choose from more than 30+ themes or create your own.\"\n    imageSrc=\"/static/images/shipixen/product/2.webp\"\n    imageAlt=\"Sample image\"\n  />\n</LandingProductFeaturesGrid>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                                                              | Prop Type                             | Required | Default       |\n| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | -------- | ------------- |\n| **children** <Tippy>React nodes to be rendered within the component, supporting a mix of `LandingProductFeature` and/or `LandingProductVideoFeature` elements.</Tippy> | `React.ReactNode`                     | No       | -             |\n| **title** <Tippy>The title text or a React node to be displayed at the top of the component.</Tippy>                                                                   | `string` ǀ `React.ReactNode`          | No       | -             |\n| **titleComponent** <Tippy>A React node used to render a custom title component.</Tippy>                                                                                | `React.ReactNode`                     | No       | -             |\n| **description** <Tippy>The description text or a React node to be displayed below the title.</Tippy>                                                                   | `string` ǀ `React.ReactNode`          | No       | -             |\n| **descriptionComponent** <Tippy>A React node used to render a custom description component.</Tippy>                                                                    | `React.ReactNode`                     | No       | -             |\n| **withBackground** <Tippy>Determines whether the background styling based on the variant is applied to the component.</Tippy>                                          | `boolean`                             | No       | `true`        |\n| **variant** <Tippy>Defines the color scheme variant for the component, affecting both background and child components.</Tippy>                                         | `'primary'` ǀ `'secondary'`           | No       | `'primary'`   |\n| **containerType** <Tippy>Specifies the width of the container, affecting its max-width on larger screens.</Tippy>                                                      | `'narrow'` ǀ `'wide'` ǀ `'ultrawide'` | No       | `'ultrawide'` |\n| **numberOfColumns** <Tippy>Specifies the number of columns to display in the grid.</Tippy>                                                                            | `number`                              | No       | -             |\n</PropsReference>"
            },
            "ProductHuntAward": {
              "description": "Use this component to show a Product Hunt award, if applicable, to increase trust.",
              "api": "<Usage>\n```jsx\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\n```\n\n```jsx\n<LandingProductHuntAward place={3} />\n```\n</Usage>\n\n<Examples>\n### Customization\n\nThe award can either be grayscale or colored and it comes in two sizes: small and default.\n\n\n```jsx\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\n\n<LandingProductHuntAward size=\"small\" grayscale={false} />\n```\n\n\n### With custom text\n\n\n```jsx\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\n\n<LandingProductHuntAward\n  title=\"#1 Product of the week\"\n  subtitle=\"Marketing\"\n/>\n```\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                               | Prop Type               | Required | Default                |\n| ----------------------------------------------------------------------------------------------------------------------- | ----------------------- | -------- | ---------------------- |\n| **place** <Tippy>The place or rank to display.</Tippy>                                                                  | `number` ǀ `string`     | Yes      | -                      |\n| **title** <Tippy>The title text displayed above the place/rank.</Tippy>                                                 | `string`                | No       | `'Product of the Day'` |\n| **size** <Tippy>Defines the size of the component, affecting its height and the size of text.</Tippy>                   | `'default'` ǀ `'small'` | No       | `'default'`            |\n| **grayscale** <Tippy>Whether the component should be displayed in grayscale colors.</Tippy>                             | `boolean`               | No       | `true`                 |\n| **textContainerClassName** <Tippy>Additional class names for styling the container of the title and place text.</Tippy> | `string`                | No       | -                      |\n| **titleClassName** <Tippy>Additional class names for styling the title text.</Tippy>                                    | `string`                | No       | -                      |\n| **placeClassName** <Tippy>Additional class names for styling the place/rank text.</Tippy>                               | `string`                | No       | -                      |\n</PropsReference>"
            },
            "ProductSteps": {
              "description": "The `LandingProductSteps` component displays a title, description, and a list of steps with alternating media positions. It's designed for showcasing product features, workflows, or processes on landing pages. This component is ideal when you need to present more than three features in a visually engaging format.",
              "api": "<Usage>\n```jsx\nimport { LandingProductSteps } from '@/components/landing/LandingProductSteps';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\n```\n\n```jsx\n<LandingProductSteps\n  title=\"Product Features\"\n  description=\"Discover what makes our product stand out.\"\n>\n  <LandingProductFeature\n    title=\"Feature One\"\n    description=\"Description of feature one\"\n    imageSrc=\"/path/to/image.webp\"\n  />\n  <LandingProductFeature\n    title=\"Feature Two\"\n    description=\"Description of feature two\"\n    imageSrc=\"/path/to/image.webp\"\n  />\n</LandingProductSteps>\n```\n</Usage>\n\n<Examples>\n### Grid Display\n\n\n\n```jsx\nimport { LandingProductSteps } from '@/components/landing/LandingProductSteps';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductSteps\n  title=\"Our Process\"\n  description=\"How we deliver quality results every time.\"\n  display=\"grid\"\n>\n  <LandingProductFeature\n    title=\"Research\"\n    description=\"We start by understanding your needs and goals.\"\n    imageSrc=\"/static/images/backdrop-1.webp\"\n  />\n  <LandingProductFeature\n    title=\"Design\"\n    description=\"Our team creates tailored solutions for your business.\"\n    imageSrc=\"/static/images/backdrop-5.webp\"\n  />\n  <LandingProductFeature\n    title=\"Code\"\n    description=\"We build your project to perfection.\"\n    imageSrc=\"/static/images/backdrop-6.webp\"\n  />\n</LandingProductSteps>\n```\n\n\n### Secondary Variant\n\n\n\n```jsx\nimport { LandingProductSteps } from '@/components/landing/LandingProductSteps';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductSteps\n  title=\"Our Process\"\n  description=\"How we deliver quality results every time.\"\n  variant=\"secondary\"\n>\n  <LandingProductFeature\n    title=\"Research\"\n    description=\"We start by understanding your needs and goals.\"\n    imageSrc=\"/static/images/backdrop-4.webp\"\n  />\n  <LandingProductFeature\n    title=\"Design\"\n    description=\"Our team creates tailored solutions for your business.\"\n    imageSrc=\"/static/images/backdrop-5.webp\"\n  />\n</LandingProductSteps>\n```\n\n\n\n### With Background Glow\n\n\n\n```jsx\nimport { LandingProductSteps } from '@/components/landing/LandingProductSteps';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductSteps\n  title=\"Platform Features\"\n  description=\"Discover the powerful capabilities of our platform.\"\n  withBackgroundGlow={true}\n>\n  <LandingProductFeature\n    title=\"Analytics Dashboard\"\n    description=\"Track key metrics and performance indicators in real-time.\"\n    imageSrc=\"/static/images/backdrop-6.webp\"\n  />\n  <LandingProductFeature\n    title=\"Team Collaboration\"\n    description=\"Work together efficiently with integrated tools.\"\n    imageSrc=\"/static/images/backdrop-7.webp\"\n  />\n</LandingProductSteps>\n```\n\n\n\n### With Video Features\n\n\n\n```jsx\nimport { LandingProductSteps } from '@/components/landing/LandingProductSteps';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\n\n<LandingProductSteps\n  title=\"See it in action\"\n  description=\"Watch how our product solves real-world problems.\"\n>\n  <LandingProductVideoFeature\n    title=\"Easy Setup\"\n    description=\"Get started in minutes with our guided setup process.\"\n    videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n    videoPoster=\"/static/images/backdrop-8.webp\"\n  />\n  <LandingProductFeature\n    title=\"Intuitive Interface\"\n    description=\"Navigate with ease through our user-friendly platform.\"\n    imageSrc=\"/static/images/backdrop-9.webp\"\n  />\n</LandingProductSteps>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **className** <Tippy>Additional CSS classes for the section container.</Tippy> | `string` | No | `undefined` |\n| **children** <Tippy>LandingProductFeature and/or LandingProductVideoFeature components to display as steps.</Tippy> | `React.ReactNode` | No | `undefined` |\n| **title** <Tippy>The main heading text for the section.</Tippy> | `string \\| React.ReactNode` | No | `undefined` |\n| **titleComponent** <Tippy>Custom React component to use instead of the default title.</Tippy> | `React.ReactNode` | No | `undefined` |\n| **description** <Tippy>Descriptive text that appears below the title.</Tippy> | `string \\| React.ReactNode` | No | `undefined` |\n| **descriptionComponent** <Tippy>Custom React component to use instead of the default description.</Tippy> | `React.ReactNode` | No | `undefined` |\n| **withBackground** <Tippy>Determines if the component has a semi-transparent background color.</Tippy> | `boolean` | No | `true` |\n| **withBackgroundGlow** <Tippy>Adds a decorative glow effect to the background.</Tippy> | `boolean` | No | `false` |\n| **variant** <Tippy>Color theme for the component, affecting background colors.</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **backgroundGlowVariant** <Tippy>Color theme for the background glow effect.</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **containerType** <Tippy>Controls the maximum width of the content container.</Tippy> | `'narrow' \\| 'wide' \\| 'ultrawide'` | No | `'ultrawide'` |\n| **display** <Tippy>Controls the display of the component.</Tippy> | `'list' \\| 'grid'` | No | `'list'` |\n</PropsReference>"
            },
            "ProductTour": {
              "description": "This component displays a list of features and content corresponding to each, creating a product tour. It is useful to showcase many features in a compact way and guide the user through the product.",
              "api": "<Usage>\n```jsx\nimport { LandingProductTourSection, LandingProductTourList, LandingProductTourTrigger, LandingProductTourContent } from '@/components/landing/LandingProductTour';\nimport { VideoPlayer } from '@/components/shared/ui/VideoPlayer';\n```\n\n```jsx\n<LandingProductTourSection\n  title='Landing page in minutes'\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  defaultValue=\"feature-1\"\n>\n  <LandingProductTourList>\n    <LandingProductTourTrigger value=\"feature-1\">\n      <p className=\"text-xl font-bold\">\n        Automatic deployment to Vercel\n      </p>\n      <p>\n        Deploying the generated template to Vercel is as easy as clicking a button.\n      </p>\n    </LandingProductTourTrigger>\n\n    <LandingProductTourTrigger value=\"feature-2\">\n      <p className=\"text-xl font-bold\">\n        MDX blog, no server required\n      </p>\n      <p>\n        Shipixen comes with a fully featured MDX blog.\n      </p>\n    </LandingProductTourTrigger>\n\n    <LandingProductTourTrigger value=\"feature-3\">\n      <p className=\"text-xl font-bold\">\n        Customizable themes\n      </p>\n      <p>\n        Choose from more than 30+ beautifully designed themes or create your own.\n      </p>\n    </LandingProductTourTrigger>\n  </LandingProductTourList>\n  <LandingProductTourContent value=\"feature-1\">\n    <VideoPlayer\n      className={'w-full rounded-md'}\n      src={'https://cache.shipixen.com/features/20-mobile-optimized.mp4'}\n      autoPlay={true}\n      controls={false}\n      loop={true}\n    />\n  </LandingProductTourContent>\n  <LandingProductTourContent value=\"feature-2\">\n    <VideoPlayer\n      className={'w-full rounded-md'}\n      src={\n        'https://cache.shipixen.com/features/11-pricing-page-builder.mp4'\n      }\n      autoPlay={true}\n      controls={false}\n      loop={true}\n    />\n  </LandingProductTourContent>\n  <LandingProductTourContent value=\"feature-3\">\n    <VideoPlayer\n      className={'w-full rounded-md'}\n      src={'https://cache.shipixen.com/features/21-run-locally.mp4'}\n      autoPlay={true}\n      controls={false}\n      loop={true}\n    />\n  </LandingProductTourContent>\n</LandingProductTourSection>\n```\n</Usage>\n\n<Examples>\n### With images\n\n\n```jsx\nimport { LandingProductTourSection, LandingProductTourList, LandingProductTourTrigger, LandingProductTourContent } from '@/components/landing/LandingProductTour';\nimport Image from '@/components/shared/Image';\n\n<LandingProductTourSection\n  title='Landing page in minutes'\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  defaultValue=\"feature-1\"\n>\n  <LandingProductTourList>\n    <LandingProductTourTrigger value=\"feature-1\">\n      <p className=\"text-xl font-bold\">\n        Automatic deployment to Vercel\n      </p>\n      <p>\n        Deploying the generated template to Vercel is as easy as clicking a button.\n      </p>\n    </LandingProductTourTrigger>\n\n    <LandingProductTourTrigger value=\"feature-2\">\n      <p className=\"text-xl font-bold\">\n        MDX blog, no server required\n      </p>\n      <p>\n        Shipixen comes with a fully featured MDX blog.\n      </p>\n    </LandingProductTourTrigger>\n\n    <LandingProductTourTrigger value=\"feature-3\">\n      <p className=\"text-xl font-bold\">\n        Customizable themes\n      </p>\n      <p>\n        Choose from more than 30+ beautifully designed themes or create your own.\n      </p>\n    </LandingProductTourTrigger>\n  </LandingProductTourList>\n  <LandingProductTourContent value=\"feature-1\">\n    <Image src=\"https://picsum.photos/id/206/800/800\" width={800} height={800} />\n  </LandingProductTourContent>\n  <LandingProductTourContent value=\"feature-2\">\n    <Image src=\"https://picsum.photos/id/33/800/800\" width={800} height={800} />\n  </LandingProductTourContent>\n  <LandingProductTourContent value=\"feature-3\">\n    <Image src=\"https://picsum.photos/id/59/800/800\" width={800} height={800} />\n  </LandingProductTourContent>\n</LandingProductTourSection>\n```\n\n\n\n### With background\n\n\n```jsx\nimport { LandingProductTourSection, LandingProductTourList, LandingProductTourTrigger, LandingProductTourContent } from '@/components/landing/LandingProductTour';\nimport { VideoPlayer } from '@/components/shared/ui/VideoPlayer';\n\n<LandingProductTourSection\n  title='Landing page in minutes'\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  defaultValue=\"feature-1\"\n  withBackground\n  variant=\"secondary\"\n>\n  <LandingProductTourList>\n    <LandingProductTourTrigger value=\"feature-1\">\n      <p className=\"text-xl font-bold\">\n        Automatic deployment to Vercel\n      </p>\n      <p>\n        Deploying the generated template to Vercel is as easy as clicking a button.\n      </p>\n    </LandingProductTourTrigger>\n\n    <LandingProductTourTrigger value=\"feature-2\">\n      <p className=\"text-xl font-bold\">\n        MDX blog, no server required\n      </p>\n      <p>\n        Shipixen comes with a fully featured MDX blog.\n      </p>\n    </LandingProductTourTrigger>\n\n    <LandingProductTourTrigger value=\"feature-3\">\n      <p className=\"text-xl font-bold\">\n        Customizable themes\n      </p>\n      <p>\n        Choose from more than 30+ beautifully designed themes or create your own.\n      </p>\n    </LandingProductTourTrigger>\n  </LandingProductTourList>\n  <LandingProductTourContent value=\"feature-1\">\n    <VideoPlayer\n      className={'w-full rounded-md'}\n      src={'https://cache.shipixen.com/features/20-mobile-optimized.mp4'}\n      autoPlay={true}\n      controls={false}\n      loop={true}\n    />\n  </LandingProductTourContent>\n  <LandingProductTourContent value=\"feature-2\">\n    <VideoPlayer\n      className={'w-full rounded-md'}\n      src={\n        'https://cache.shipixen.com/features/11-pricing-page-builder.mp4'\n      }\n      autoPlay={true}\n      controls={false}\n      loop={true}\n    />\n  </LandingProductTourContent>\n  <LandingProductTourContent value=\"feature-3\">\n    <VideoPlayer\n      className={'w-full rounded-md'}\n      src={'https://cache.shipixen.com/features/21-run-locally.mp4'}\n      autoPlay={true}\n      controls={false}\n      loop={true}\n    />\n  </LandingProductTourContent>\n</LandingProductTourSection>\n```\n\n\n\n### With background glow\n\n\n```jsx\nimport { LandingProductTourSection, LandingProductTourList, LandingProductTourTrigger, LandingProductTourContent } from '@/components/landing/LandingProductTour';\nimport { VideoPlayer } from '@/components/shared/ui/VideoPlayer';\n\n<LandingProductTourSection\n  title='Landing page in minutes'\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  defaultValue=\"feature-1\"\n  withBackgroundGlow\n>\n  <LandingProductTourList>\n    <LandingProductTourTrigger value=\"feature-1\">\n      <p className=\"text-xl font-bold\">\n        Automatic deployment to Vercel\n      </p>\n      <p>\n        Deploying the generated template to Vercel is as easy as clicking a button.\n      </p>\n    </LandingProductTourTrigger>\n\n    <LandingProductTourTrigger value=\"feature-2\">\n      <p className=\"text-xl font-bold\">\n        MDX blog, no server required\n      </p>\n      <p>\n        Shipixen comes with a fully featured MDX blog.\n      </p>\n    </LandingProductTourTrigger>\n\n    <LandingProductTourTrigger value=\"feature-3\">\n      <p className=\"text-xl font-bold\">\n        Customizable themes\n      </p>\n      <p>\n        Choose from more than 30+ beautifully designed themes or create your own.\n      </p>\n    </LandingProductTourTrigger>\n  </LandingProductTourList>\n  <LandingProductTourContent value=\"feature-1\">\n    <VideoPlayer\n      className={'w-full rounded-md'}\n      src={'https://cache.shipixen.com/features/20-mobile-optimized.mp4'}\n      autoPlay={true}\n      controls={false}\n      loop={true}\n    />\n  </LandingProductTourContent>\n  <LandingProductTourContent value=\"feature-2\">\n    <VideoPlayer\n      className={'w-full rounded-md'}\n      src={\n        'https://cache.shipixen.com/features/11-pricing-page-builder.mp4'\n      }\n      autoPlay={true}\n      controls={false}\n      loop={true}\n    />\n  </LandingProductTourContent>\n  <LandingProductTourContent value=\"feature-3\">\n    <VideoPlayer\n      className={'w-full rounded-md'}\n      src={'https://cache.shipixen.com/features/21-run-locally.mp4'}\n      autoPlay={true}\n      controls={false}\n      loop={true}\n    />\n  </LandingProductTourContent>\n</LandingProductTourSection>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name | Prop Type | Required | Default |\n| --------- | --------- | -------- | ------- |\n| **title** <Tippy>Text content for the section's title.</Tippy> | `string` ǀ `React.ReactNode` | No | - |\n| **titleComponent** <Tippy>React node to render a custom title component.</Tippy> | `React.ReactNode` | No | - |\n| **description** <Tippy>Text content for the section's description.</Tippy> | `string` ǀ `React.ReactNode` | No | - |\n| **descriptionComponent** <Tippy>React node to render a custom description component.</Tippy> | `React.ReactNode` | No | - |\n| **withBackground** <Tippy>Determines whether the section has a background.</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Determines whether the section has a glowing background effect.</Tippy> | `boolean` | No | `false` |\n| **variant** <Tippy>Specifies the variant of the background.</Tippy> | `'primary'` ǀ `'secondary'` | No | `'primary'` |\n| **backgroundGlowVariant** <Tippy>Specifies the variant of the glowing background effect.</Tippy> | `'primary'` ǀ `'secondary'` | No | `'primary'` |\n</PropsReference>"
            },
            "ProductVideoFeature": {
              "description": "This component is used to display a product video feature in the landing page. The video could either be left, right or center (larger). The section can have a background or not. It displays a title, description and video of a product's feature. Use this to highlight a feature or key aspect of your product with a video. Can be used with multiple features in a [Product Features Grid](/boilerplate-documentation/landing-page-components/product-features-grid).",
              "api": "<Usage>\n```jsx\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\n```\n\n```jsx\n<LandingProductVideoFeature\n  title=\"Sites in minutes\"\n  description=\"Choose from more than 30+ themes or create your own.\"\n  videoSrc=\"https://cache.shipixen.com/features/3-theme-and-logo.mp4\"\n/>\n```\n</Usage>\n\n<Examples>\n### Video Position\n\n\n\n```jsx\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\n\n<LandingProductVideoFeature\n  textPosition=\"center\"\n  videoPosition=\"center\"\n  withBackground\n  variant=\"primary\"\n  title=\"Sites in minutes\"\n  description=\"Choose from more than 30+ themes or create your own.\"\n  autoPlay={false}\n  controls={false}\n  videoSrc=\"https://cache.shipixen.com/features/3-theme-and-logo.mp4\"\n/>;\n```\n\n\n\n### Customization\n\nTo better separate sections, you can alternate between primary, secondary\nand no background.<br />\nHere we set <b>variant</b> to <b>secondary</b> and the <b>videoPosition</b> to <b>right</b>.\n\n\n\n```jsx\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\n\n<LandingProductVideoFeature\n  videoPosition=\"right\"\n  withBackground\n  variant=\"secondary\"\n  title=\"Sites in minutes\"\n  description=\"Choose from more than 30+ themes or create your own.\"\n  autoPlay={false}\n  controls={false}\n  videoSrc=\"https://cache.shipixen.com/features/3-theme-and-logo.mp4\"\n/>;\n```\n\n\n\n### With Background Glow\n\n\n\n```jsx\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\n\n<LandingProductVideoFeature\n  videoPosition=\"left\"\n  withBackgroundGlow\n  backgroundGlowVariant=\"primary\"\n  title=\"Easy Branding\"\n  description=\"Choose from more than 30+ themes or create your own. Upload your logo and we take care of the rest.\"\n  autoPlay={false}\n  controls={false}\n  videoSrc=\"https://cache.shipixen.com/features/3-theme-and-logo.mp4\"\n/>;\n```\n\n\n\n### With Bullet Points\n\n\n\n```jsx\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\nimport { LandingProductFeatureKeyPoints } from '@/components/landing/LandingProductFeatureKeyPoints';\n\n<LandingProductVideoFeature\n  withBackgroundGlow\n  backgroundGlowVariant=\"primary\"\n  title=\"Easy Branding\"\n  autoPlay={false}\n  controls={false}\n  videoSrc=\"https://cache.shipixen.com/features/3-theme-and-logo.mp4\"\n  descriptionComponent={\n    <>\n      <LandingProductFeatureKeyPoints\n        keyPoints={[\n          {\n            title: 'Intelligent Assistance',\n            description: 'Receive personalized recommendations.',\n          },\n          {\n            title: 'Seamless Collaboration',\n            description: 'Easily collaborate with team members.',\n          },\n          {\n            title: 'Advanced Customization',\n            description: 'Tailor your app to fit your style.',\n          },\n        ]}\n      />\n    </>\n  }\n/>;\n```\n\n\n\n### With Call to Action (CTA)\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingProductFeature } from '@/components/landing/LandingProductFeature';\n\n<LandingProductVideoFeature\n  withBackgroundGlow\n  backgroundGlowVariant=\"primary\"\n  title=\"Easy Branding\"\n  autoPlay={false}\n  controls={false}\n  videoSrc=\"https://cache.shipixen.com/features/3-theme-and-logo.mp4\"\n  descriptionComponent={\n    <>\n      <p>\n        Receive personalized recommendations and insights tailored to your\n        workflow and easily collaborate with team members and clients in\n        real-time.\n      </p>\n\n      <Button className=\"mt-8\" asChild>\n        <a href=\"#\">Try now for free</a>\n      </Button>\n\n      <p className=\"text-sm opacity-70\">\n        7 day free trial, no credit card required.\n      </p>\n    </>\n  }\n/>;\n```\n\n\n\n### With Features Grid\n\n\n\n```jsx\nimport { LandingProductFeaturesGrid } from '@/components/landing/LandingProductFeaturesGrid';\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\n\n<LandingProductFeaturesGrid\n  title=\"Get the job done in no time\"\n  description=\"You'll save days of work and the only question you'll have is 'What do I do with all this free time?'\"\n>\n  <LandingProductVideoFeature\n    title=\"Generate\"\n    description=\"Save time by generating features, sales copy and more.\"\n    autoPlay={false}\n    videoSrc=\"https://cache.shipixen.com/features/2-generate-content-with-ai.mp4\"\n  />\n\n  <LandingProductVideoFeature\n    title=\"Design\"\n    description=\"Choose from more than 30+ themes or create your own.\"\n    autoPlay={false}\n    videoSrc=\"https://cache.shipixen.com/features/3-theme-and-logo.mp4\"\n  />\n\n  <LandingProductVideoFeature\n    title=\"Build\"\n    description=\"Use our pricing page builder to create a beautiful pricing page.\"\n    autoPlay={false}\n    videoSrc=\"https://cache.shipixen.com/features/11-pricing-page-builder.mp4\"\n  />\n</LandingProductFeaturesGrid>;\n```\n\n\n\n### With Product Steps\n\n\n\n```jsx\nimport { LandingProductSteps } from '@/components/landing/LandingProductSteps';\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\n\n<LandingProductSteps\n  title=\"See it in action\"\n  description=\"Watch how our product solves real-world problems.\"\n>\n  <LandingProductVideoFeature\n    title=\"Easy Setup\"\n    description=\"Get started in minutes with our guided setup process.\"\n    videoSrc=\"https://cache.shipixen.com/features/8-customize-pages.mp4\"\n    videoPoster=\"/static/images/backdrop-8.webp\"\n  />\n  <LandingProductVideoFeature\n    title=\"Intuitive Interface\"\n    description=\"Navigate with ease through our user-friendly platform.\"\n    videoSrc=\"https://cache.shipixen.com/features/11-pricing-page-builder.mp4\"\n    videoPoster=\"/static/images/backdrop-8.webp\"\n  />\n</LandingProductSteps>\n```\n\n\n\n### With Background Effect\n\n\n\n```jsx\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\nimport { LandingDotParticleCtaBg } from '@/components/landing/cta-backgrounds/LandingDotParticleCtaBg';\n\n<LandingProductVideoFeature\n  title=\"The wait is over\"\n  description=\"Give your project the home it deserves. Your users will love you for it.\"\n  videoSrc=\"https://cache.shipixen.com/features/3-theme-and-logo.mp4\"\n  videoPoster=\"/static/images/backdrop-5.webp\"\n  effectComponent={<LandingDotParticleCtaBg />}\n/>;\n```\n\n\n\n\n### With Leading Pill\n\n\n\n```jsx\nimport { LandingProductVideoFeature } from '@/components/landing/LandingProductVideoFeature';\n\n<LandingProductVideoFeature\n  title=\"The wait is over\"\n  description=\"Give your project the home it deserves. Your users will love you for it.\"\n  videoSrc=\"https://cache.shipixen.com/features/3-theme-and-logo.mp4\"\n  videoPoster=\"/static/images/backdrop-5.webp\"\n  leadingComponent={\n    <LandingLeadingPill\n      withBorder={false}\n      withBackground={true}\n      backgroundVariant=\"primary\"\n      leftComponent={<SparklesIcon className=\"w-4 h-4\" />}\n    >\n      Join today\n    </LandingLeadingPill>\n  }\n/>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                             | Prop Type                         | Required | Default     |\n| ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | -------- | ----------- |\n| **children** <Tippy>React nodes to be rendered within the component (below the text on smaller screen, right side on larger).</Tippy> | `React.ReactNode` ǀ `string`      | No       | -           |\n| **className** <Tippy>Additional CSS classes to apply to the component.</Tippy>                                                        | `string`                          | No       | -           |\n| **innerClassName** <Tippy>Additional CSS classes to apply to the inner container of the component.</Tippy>                            | `string`                          | No       | -           |\n| **title** <Tippy>Title of the section.</Tippy>                                                                                        | `string` ǀ `React.ReactNode`      | No       | -           |\n| **titleComponent** <Tippy>Custom React component for the title.</Tippy>                                                               | `React.ReactNode`                 | No       | -           |\n| **description** <Tippy>Description of the section.</Tippy>                                                                            | `string` ǀ `React.ReactNode`      | No       | -           |\n| **descriptionComponent** <Tippy>Custom React component for the description.</Tippy>                                                   | `React.ReactNode`                 | No       | -           |\n| **leadingComponent** <Tippy>Custom React component to render as the leading component.</Tippy>                                         | `React.ReactNode`                                                        | No       | -           |\n| **textPosition** <Tippy>Position of the text content. Can be `'center'` or `'left'`.</Tippy>                                          | `'center'` ǀ `'left'`             | No       | `'left'`    |\n| **videoSrc** <Tippy>Source URL for the video.</Tippy>                                                                                 | `string`                          | No       | -           |\n| **videoPoster** <Tippy>URL for the poster image of the video.</Tippy>                                                                 | `string`                          | No       | -           |\n| **videoPosition** <Tippy>Position of the video. Can be `'left'`, `'right'`, or `'center'`.</Tippy>                                    | `'left'` ǀ `'right'` ǀ `'center'` | No       | `'right'`   |\n| **videoMaxWidth** <Tippy>Maximum width of the video.</Tippy>                                                                          | `string`                          | No       | `'none'`    |\n| **autoPlay** <Tippy>Specifies whether the video should automatically start playing.</Tippy>                                           | `boolean`                         | No       | -           |\n| **controls** <Tippy>Specifies whether the video player should display controls.</Tippy>                                               | `boolean`                         | No       | `false`     |\n| **zoomOnHover** <Tippy>Specifies whether the video should zoom on hover.</Tippy>                                                      | `boolean`                         | No       | `false`     |\n| **minHeight** <Tippy>Minimum height of the section.</Tippy>                                                                           | `number`                          | No       | `350`       |\n| **withBackground** <Tippy>Specifies whether the section should have a background.</Tippy>                                             | `boolean`                         | No       | `false`     |\n| **withBackgroundGlow** <Tippy>Specifies whether the section should have a glowing background.</Tippy>                                 | `boolean`                         | No       | `false`     |\n| **variant** <Tippy>Variation style of the section. Can be `'primary'` or `'secondary'`.</Tippy>                                       | `'primary'` ǀ `'secondary'`       | No       | `'primary'` |\n| **backgroundGlowVariant** <Tippy>Variation style of the glowing background.</Tippy>                                                   | `'primary'` ǀ `'secondary'`       | No       | -           |\n| **effectComponent** <Tippy>Custom React component to be displayed as the background effect.</Tippy>                                    | `React.ReactNode`                                                        | No       | -           |\n| **effectClassName** <Tippy>Additional CSS classes to apply to the background effect.</Tippy>                                        | `string`                                                                 | No       | -           |\n</PropsReference>"
            },
            "Rating": {
              "description": "Use this component to show a rating with stars.",
              "api": "<Usage>\n```jsx\nimport { LandingRating } from '@/components/landing/rating/LandingRating';\n```\n\n```jsx\n<LandingRating />\n```\n</Usage>\n\n<Examples>\n### With Custom Rating\n\n\n\n```jsx\nimport { LandingRating } from '@/components/landing/rating/LandingRating';\n\n<LandingRating rating={4} />;\n```\n\n\n\n### With Partial Rating\n\n\n\n```jsx\nimport { LandingRating } from '@/components/landing/rating/LandingRating';\n\n<LandingRating rating={4.3} />;\n```\n\n\n\n### With Custom Size\n\n\n\n```jsx\nimport { LandingRating } from '@/components/landing/rating/LandingRating';\n\n<LandingRating rating={4.3} size=\"large\" />;\n```\n\n\n\n### With Custom Max Rating\n\n\n\n```jsx\nimport { LandingRating } from '@/components/landing/rating/LandingRating';\n\n<LandingRating rating={4.3} maxRating={6} />;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                            | Prop Type                         | Required | Default    |\n| -------------------------------------------------------------------- | --------------------------------- | -------- | ---------- |\n| **rating** <Tippy>Number representing the current rating.</Tippy>    | `number`                          | No       | 5          |\n| **maxRating** <Tippy>Number representing the maximum rating.</Tippy> | `number`                          | No       | 5          |\n| **size** <Tippy>Size of the rating stars.</Tippy>                    | `'small'   ǀ 'medium'  ǀ 'large'` | No       | `'medium'` |\n</PropsReference>"
            },
            "SaleCta": {
              "description": "Use this to prompt users to take action, such as signing up for a trial or buying a product. <br /> This can be used to break up longer pages and increase conversion as users scroll down and get past your primary CTA.",
              "api": "<Usage>\n```jsx\nimport { LandingSaleCtaSection } from '@/components/landing/cta/LandingSaleCta';\n```\n\n```\n<LandingSaleCtaSection\n  title=\"Ready to get started?\"\n  description={\n    'Pre-order today and get a 50% discount on the final price for the first 3 months. No credit card required.'\n  }\n  ctaHref=\"https://gum.co/product\"\n  ctaLabel=\"Pre-order for $49\"\n/>\n```\n</Usage>\n\n<Examples>\n### Background Customization/Variant\n\n\n\n```jsx\nimport { LandingSaleCtaSection } from '@/components/landing/cta/LandingSaleCta';\n\n<LandingSaleCtaSection\n  withBackground\n  variant=\"secondary\"\n  title=\"Ready to get started?\"\n  description=\"Pre-order today and get a 50% discount on the final price for the first 3 months. No credit card required.\"\n  ctaHref=\"https://gum.co/product\"\n  ctaLabel=\"Pre-order for $49\"\n/>;\n```\n\n\n\n### Secondary Call to Action\n\n\n\n```jsx\nimport { LandingSaleCtaSection } from '@/components/landing/cta/LandingSaleCta';\n\n<LandingSaleCtaSection\n  withBackground\n  title=\"Ready to get started?\"\n  description='Pre-order today and get a 50% discount on the final price for the first 3 months. No credit card required.'\n  ctaHref='https://gum.co/product'\n  ctaLabel='Pre-order for $49'\n  secondaryCtaHref='https://gum.co/learn-more'\n  secondaryCtaLabel='Learn more'\n/>\n```\n\n\n\n### Custom CTAs\n\n\n\n```jsx\nimport { LandingSaleCtaSection } from '@/components/landing/cta/LandingSaleCta';\nimport { Button } from '@/components/ui/button';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\n\n<LandingSaleCtaSection\n  withBackground\n  title=\"Ready to get started?\"\n  description='Pre-order today and get a 50% discount on the final price for the first 3 months. No credit card required.'\n  variant=\"secondary\"\n>\n  <Button size=\"xl\" variant=\"secondary\" asChild>\n    <a href=\"#\">Buy Now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlineSecondary\">\n    <a href=\"#\">Learn More</a>\n  </Button>\n\n  <LandingDiscount\n    className=\"w-full\"\n    discountValueText=\"$350 off\"\n    discountDescriptionText=\"for the first 10 customers (2 left)\"\n  />\n</LandingSaleCtaSection>\n```\n\n\n\n### Background Glow\n\n\n\n```jsx\nimport { LandingSaleCtaSection } from '@/components/landing/cta/LandingSaleCta';\n\n<LandingSaleCtaSection\n  withBackgroundGlow\n  variant=\"secondary\"\n  backgroundGlowVariant=\"secondary\"\n  title=\"Ready to get started?\"\n  description=\"Pre-order today and get a 50% discount on the final price for the first 3 months. No credit card required.\"\n  ctaHref=\"https://gum.co/product\"\n  ctaLabel=\"Pre-order for $49\"\n/>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                            | Prop Type                    | Required | Default     |\n| -------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -------- | ----------- |\n| **children** <Tippy>React nodes to be rendered within the component.</Tippy>                                         | `React.ReactNode`            | No       | -           |\n| **className**                                                                                                        | `string`                     | No       | -           |\n| **title** <Tippy>A string or React nodes representing the title of the section.</Tippy>                              | `string` ǀ `React.ReactNode` | No       | -           |\n| **titleComponent** <Tippy>A React node representing the title of the section.</Tippy>                                | `React.ReactNode`            | No       | -           |\n| **description** <Tippy>A string or React nodes representing the description of the section.</Tippy>                  | `string` ǀ `React.ReactNode` | No       | -           |\n| **descriptionComponent** <Tippy>A React node representing the description of the section.</Tippy>                    | `React.ReactNode`            | No       | -           |\n| **footerComponent** <Tippy>A React node to be rendered as the footer of the section.</Tippy>                         | `React.ReactNode`            | No       | -           |\n| **ctaHref**                                                                                                          | `string`                     | No       | `'#'`       |\n| **ctaLabel**                                                                                                         | `string`                     | No       | -           |\n| **secondaryCtaHref**                                                                                                 | `string`                     | No       | `'#'`       |\n| **secondaryCtaLabel**                                                                                                | `string`                     | No       | -           |\n| **withBackground** <Tippy>Indicates whether the section should have a background or not.</Tippy>                     | `boolean`                    | No       | `false`     |\n| **withBackgroundGlow** <Tippy>Indicates whether the section should have a glowing background or not.</Tippy>         | `boolean`                    | No       | `false`     |\n| **variant** <Tippy>The variant of the section. Can be `'primary'` or `'secondary'`.</Tippy>                          | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n| **backgroundGlowVariant** <Tippy>The variant of the glowing background. Can be `'primary'` or `'secondary'`.</Tippy> | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n</PropsReference>"
            },
            "Showcase": {
              "description": "This component displays a grid of logos or images, showcasing the companies that use the product, integrations, etc.",
              "api": "<Usage>\n```jsx\nimport {\n  FigmaIcon,\n  TwitchIcon,\n  ChromeIcon,\n  InstagramIcon,\n  TwitterIcon,\n  FramerIcon,\n  GithubIcon,\n  SlackIcon,\n} from 'lucide-react';\nimport { LandingShowcase } from '@/components/landing/showcase/LandingShowcase';\nimport { LandingShowcaseItem } from '@/components/landing/showcase/LandingShowcaseItem';\n```\n\n```jsx\n<LandingShowcase\n  title=\"Import with ease\"\n  description=\"All your video assets in one platform. Import your existing footage from any device with a click.\"\n>\n  <LandingShowcaseItem>\n    <FigmaIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <TwitchIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <ChromeIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <InstagramIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <TwitterIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <FramerIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <GithubIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <SlackIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n</LandingShowcase>\n```\n</Usage>\n\n<Examples>\n### Text Position\n\n\n\n```jsx\nimport {\n  FigmaIcon,\n  TwitchIcon,\n  ChromeIcon,\n  InstagramIcon,\n  TwitterIcon,\n  FramerIcon,\n  GithubIcon,\n  SlackIcon,\n} from 'lucide-react';\nimport { LandingShowcase } from '@/components/landing/showcase/LandingShowcase';\nimport { LandingShowcaseItem } from '@/components/landing/showcase/LandingShowcaseItem';\n\n<LandingShowcase\n  title=\"Import with ease\"\n  description=\"All your video assets in one platform. Import your existing footage from any device with a click.\"\n  textPosition=\"right\"\n>\n  <LandingShowcaseItem>\n    <FigmaIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <TwitchIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <ChromeIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <InstagramIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <TwitterIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <FramerIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <GithubIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <SlackIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n</LandingShowcase>;\n```\n\n\n\n### Center Text Position\n\n\n\n```jsx\nimport {\n  FigmaIcon,\n  TwitchIcon,\n  ChromeIcon,\n  InstagramIcon,\n  TwitterIcon,\n  FramerIcon,\n  GithubIcon,\n  SlackIcon,\n} from 'lucide-react';\nimport { LandingShowcase } from '@/components/landing/showcase/LandingShowcase';\nimport { LandingShowcaseItem } from '@/components/landing/showcase/LandingShowcaseItem';\n\n<LandingShowcase\n  title=\"Import with ease\"\n  description=\"All your video assets in one platform. Import your existing footage from any device with a click.\"\n  textPosition=\"center\"\n>\n  <LandingShowcaseItem>\n    <FigmaIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <TwitchIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <ChromeIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <InstagramIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <TwitterIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <FramerIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <GithubIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <SlackIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n</LandingShowcase>\n```\n\n\n\n### With Background\n\n\n\n```jsx\nimport {\n  FigmaIcon,\n  TwitchIcon,\n  ChromeIcon,\n  InstagramIcon,\n  TwitterIcon,\n  FramerIcon,\n  GithubIcon,\n  SlackIcon,\n} from 'lucide-react';\nimport { LandingShowcase } from '@/components/landing/showcase/LandingShowcase';\nimport { LandingShowcaseItem } from '@/components/landing/showcase/LandingShowcaseItem';\n\n<LandingShowcase\n  title=\"Import with ease\"\n  description=\"All your video assets in one platform. Import your existing footage from any device with a click.\"\n  withBackground\n  variant=\"secondary\"\n>\n  <LandingShowcaseItem>\n    <FigmaIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <TwitchIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <ChromeIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <InstagramIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <TwitterIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <FramerIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <GithubIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <SlackIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n</LandingShowcase>;\n```\n\n\n\n### Background Glow\n\n\n\n```jsx\nimport {\n  FigmaIcon,\n  TwitchIcon,\n  ChromeIcon,\n  InstagramIcon,\n  TwitterIcon,\n  FramerIcon,\n  GithubIcon,\n  SlackIcon,\n} from 'lucide-react';\nimport { LandingShowcase } from '@/components/landing/showcase/LandingShowcase';\nimport { LandingShowcaseItem } from '@/components/landing/showcase/LandingShowcaseItem';\n\n<LandingShowcase\n  title=\"Import with ease\"\n  description=\"All your video assets in one platform. Import your existing footage from any device with a click.\"\n  withBackground\n  variant=\"secondary\"\n  withBackgroundGlow\n  backgroundGlowVariant=\"secondary\"\n>\n  <LandingShowcaseItem>\n    <FigmaIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <TwitchIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <ChromeIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <InstagramIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <TwitterIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <FramerIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <GithubIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n\n  <LandingShowcaseItem>\n    <SlackIcon className=\"w-10 h-10\" />\n  </LandingShowcaseItem>\n</LandingShowcase>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                             | Prop Type                    | Required | Default     |\n| ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -------- | ----------- |\n| **children** <Tippy>React nodes to be rendered within the component (below the text on smaller screen, right side on larger).</Tippy> | `React.ReactNode`            | No       | -           |\n| **innerClassName** <Tippy>Additional class names for the inner div element.</Tippy>                                                   | `string`                     | No       | -           |\n| **title** <Tippy>The title text or React node to be displayed in the component.</Tippy>                                               | `string` ǀ `React.ReactNode` | No       | -           |\n| **titleComponent** <Tippy>React node to be rendered as the title, if `title` is not provided.</Tippy>                                 | `React.ReactNode`            | No       | -           |\n| **description** <Tippy>The description text or React node to be displayed in the component.</Tippy>                                   | `string` ǀ `React.ReactNode` | No       | -           |\n| **descriptionComponent** <Tippy>React node to be rendered as the description, if `description` is not provided.</Tippy>               | `React.ReactNode`            | No       | -           |\n| **textPosition** <Tippy>Position of the text within the component. Can be 'left' or 'right' or 'center'.</Tippy>                                  | `'left'` ǀ `'right'`         | No       | `'left'`    |\n| **withBackground** <Tippy>Flag indicating whether the background should be applied.</Tippy>                                           | `boolean`                    | No       | `false`     |\n| **withBackgroundGlow** <Tippy>Flag indicating whether the background glow should be applied.</Tippy>                                  | `boolean`                    | No       | `false`     |\n| **variant** <Tippy>Variant of the background style. Can be 'primary' or 'secondary'.</Tippy>                                          | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n| **backgroundGlowVariant** <Tippy>Variant of the background glow style. Can be 'primary' or 'secondary'.</Tippy>                       | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n</PropsReference>"
            },
            "SocialProofBandItem": {
              "description": "A component meant to be used in the landing page, as a child of Social Proof Band. Shows a social proof/key feature/milestone item with an optional graphic.",
              "api": "<Usage>\n```jsx\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n```\n\n```jsx\n<LandingSocialProofBandItem>\n  100% encrypted and secure\n</LandingSocialProofBandItem>\n```\n</Usage>\n\n<Examples>\n### With custom graphics\n\n\n\n```jsx\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<LandingSocialProofBandItem graphic=\"magic\">\n  Easy setup\n</LandingSocialProofBandItem>;\n```\n\n\n\n\n\n```jsx\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<LandingSocialProofBandItem graphic=\"rating\">\n  Trusted by 1000+ customers\n</LandingSocialProofBandItem>;\n```\n\n\n\n\n\n```jsx\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<LandingSocialProofBandItem graphic=\"gift\">\n  20% off today\n</LandingSocialProofBandItem>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                             | Prop Type                                                                 | Required | Default       |\n| ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | -------- | ------------- |\n| **children** <Tippy>React nodes to be rendered within the component (below the text on smaller screen, right side on larger).</Tippy> | `React.ReactNode` ǀ `string`                                              | Yes      | -             |\n| **graphic** <Tippy>Specifies the type of graphic to display alongside the content.</Tippy>                                            | `'none'` ǀ `'checkmark'` ǀ `'gift'` ǀ `'magic'` ǀ `'trophy'` ǀ `'rating'` ǀ `'zap'` ǀ `'rocket'` ǀ `'time'` | No       | `'checkmark'` |\n| **customGraphic** <Tippy>Custom SVG graphic to display alongside the content.</Tippy>                                                 | `React.ReactNode`                                                         | No       | -             |\n</PropsReference>"
            },
            "SocialProofBand": {
              "description": "Use this to highlight key features or social proof. This is usually placed at the top of the page, but you can also use it in between sections or below your primary CTA.",
              "api": "<Usage>\n```jsx\nimport { LandingSocialProofBand } from '@/components/landing/social-proof/LandingSocialProofBand';\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n```\n\n```jsx\n<LandingSocialProofBand>\n  <LandingSocialProofBandItem>\n    100% encrypted and secure\n  </LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>24/7 customer support</LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>\n    99% customer satisfaction\n  </LandingSocialProofBandItem>\n</LandingSocialProofBand>\n```\n</Usage>\n\n<Examples>\n### Inverted\n\n\n\n```jsx\nimport { LandingSocialProofBand } from '@/components/landing/social-proof/LandingSocialProofBand';\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<LandingSocialProofBand invert={true}>\n  <LandingSocialProofBandItem>\n    100% encrypted and secure\n  </LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>24/7 customer support</LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>\n    99% customer satisfaction\n  </LandingSocialProofBandItem>\n</LandingSocialProofBand>;\n```\n\n\n\n### With customized icons\n\n\n\n```jsx\nimport { LandingSocialProofBand } from '@/components/landing/social-proof/LandingSocialProofBand';\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<LandingSocialProofBand invert={true}>\n  <LandingSocialProofBandItem graphic=\"magic\">\n    AI powered\n  </LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem graphic=\"gift\">\n    30% off this week\n  </LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem graphic=\"rating\">\n    Trusted by 1000+ customers\n  </LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem graphic=\"trophy\">\n    Most popular app in US\n  </LandingSocialProofBandItem>\n</LandingSocialProofBand>;\n```\n\n\n\n### With Primary Image CTA\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\nimport { LandingSocialProofBand } from '@/components/landing/social-proof/LandingSocialProofBand';\nimport { LandingSocialProofBandItem } from '@/components/landing/social-proof/LandingSocialProofBandItem';\n\n<LandingSocialProofBand>\n  <LandingSocialProofBandItem>\n    100% encrypted and secure\n  </LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>24/7 customer support</LandingSocialProofBandItem>\n\n  <LandingSocialProofBandItem>\n    99% customer satisfaction\n  </LandingSocialProofBandItem>\n</LandingSocialProofBand>\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  leadingComponent={<LandingProductHuntAward />}\n  withBackground\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">\n      Buy now\n    </a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n</LandingPrimaryImageCtaSection>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                             | Prop Type                    | Required | Default |\n| ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -------- | ------- |\n| **children** <Tippy>React nodes to be rendered within the component (below the text on smaller screen, right side on larger).</Tippy> | `React.ReactNode` ǀ `string` | Yes      | -       |\n| **invert** <Tippy>Whether to invert the color scheme of the component.</Tippy>                                                        | `boolean`                    | No       | -       |\n| **variant** <Tippy>The variant of the component.</Tippy>                                                                              | `'default'` ǀ `'primary'` ǀ `'secondary'` | No       | `'default'` |\n</PropsReference>"
            },
            "SocialProof": {
              "description": "Use this to show proof of existing, happy customers & increase trust. Shows social proof with avatars, number of users and an optional rating.",
              "api": "<Usage>\n```jsx\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\n```\n\n```jsx\nconst avatarItems = [\n  {\n    imageSrc: '/static/images/people/1.webp',\n    name: 'John Doe',\n  },\n  {\n    imageSrc: '/static/images/people/2.webp',\n    name: 'Jane Doe',\n  },\n  {\n    imageSrc: '/static/images/people/3.webp',\n    name: 'Alice Doe',\n  },\n  {\n    imageSrc: '/static/images/people/4.webp',\n    name: 'Bob Doe',\n  },\n  {\n    imageSrc: '/static/images/people/5.webp',\n    name: 'Eve Doe',\n  },\n];\n```\n\n```jsx\n<LandingSocialProof numberOfUsers={99} avatarItems={avatarItems} />\n```\n</Usage>\n\n<Examples>\n### With Rating\n\n\n\n```jsx\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\n\nconst avatarItems = [\n  {\n    imageSrc: '/static/images/people/1.webp',\n    name: 'John Doe',\n  },\n  {\n    imageSrc: '/static/images/people/2.webp',\n    name: 'Jane Doe',\n  },\n  {\n    imageSrc: '/static/images/people/3.webp',\n    name: 'Alice Doe',\n  },\n]\n\n<LandingSocialProof\n  showRating\n  numberOfUsers={99}\n  avatarItems={avatarItems}\n/>\n```\n\n\n\n### With Custom Suffix Text\n\n\n\n```jsx\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\n\nconst avatarItems = [\n  {\n    imageSrc: '/static/images/people/1.webp',\n    name: 'John Doe',\n  },\n  {\n    imageSrc: '/static/images/people/2.webp',\n    name: 'Jane Doe',\n  },\n  {\n    imageSrc: '/static/images/people/3.webp',\n    name: 'Alice Doe',\n  },\n]\n\n<LandingSocialProof\n  suffixText=\"experienced developers\"\n  showRating\n  numberOfUsers={99}\n  avatarItems={avatarItems}\n/>\n```\n\n\n\n### Without Hover Animation\n\n\n\n```jsx\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\n\nconst avatarItems = [\n  {\n    imageSrc: '/static/images/people/1.webp',\n    name: 'John Doe',\n  },\n  {\n    imageSrc: '/static/images/people/2.webp',\n    name: 'Jane Doe',\n  },\n  {\n    imageSrc: '/static/images/people/3.webp',\n    name: 'Alice Doe',\n  },\n  {\n    imageSrc: '/static/images/people/4.webp',\n    name: 'Bob Doe',\n  },\n  {\n    imageSrc: '/static/images/people/5.webp',\n    name: 'Eve Doe',\n  },\n]\n\n<LandingSocialProof\n  disableAnimation\n  numberOfUsers={12000}\n  avatarItems={avatarItems}\n/>\n```\n\n\n\n### With Primary Image Cta\n\n\n\n```jsx\nimport { Button } from '@/components/shared/ui/button';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\nimport { LandingDiscount } from '@/components/landing/discount/LandingDiscount';\nimport { LandingProductHuntAward } from '@/components/landing/social-proof/LandingProductHuntAward';\nimport { LandingSocialProof } from '@/components/landing/social-proof/LandingSocialProof';\n\n<LandingPrimaryImageCtaSection\n  title=\"Landing page in minutes\"\n  description=\"Get 10x more done with Shadcn UI, React & Next.js, and say goodbye to repetitive tasks. You'll never go back.\"\n  imageSrc=\"/static/images/shipixen/product/1.webp\"\n  imageAlt=\"Sample image\"\n  withBackground\n  withBackgroundGlow\n  leadingComponent={<LandingProductHuntAward />}\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Buy now</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">Read more</a>\n  </Button>\n\n  <LandingSocialProof\n    className=\"w-full mt-12\"\n    showRating\n    numberOfUsers={99}\n    avatarItems={[\n      {\n        imageSrc: '/static/images/people/1.webp',\n        name: 'John Doe',\n      },\n      {\n        imageSrc: '/static/images/people/2.webp',\n        name: 'Jane Doe',\n      },\n      {\n        imageSrc: '/static/images/people/3.webp',\n        name: 'Alice Doe',\n      },\n    ]}\n  />\n</LandingPrimaryImageCtaSection>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                     | Prop Type           | Required | Default       |\n| ------------------------------------------------------------------------------------------------------------- | ------------------- | -------- | ------------- |\n| **avatarItems** <Tippy>Array of objects representing social proof items with avatar images and names.</Tippy> | `SocialProofItem[]` | Yes      | -             |\n| **numberOfUsers** <Tippy>Number of users displayed as social proof.</Tippy>                                   | `number`            | Yes      | -             |\n| **suffixText** <Tippy>Text to be appended after the number of users, e.g., 'happy users'.</Tippy>             | `string`            | No       | 'happy users' |\n| **showRating** <Tippy>Boolean to determine whether to display the rating.</Tippy>                             | `boolean`           | No       | -             |\n| **disableAnimation** <Tippy>Boolean to disable animation.</Tippy>                                             | `boolean`           | No       | -             |\n\n```ts\nexport interface SocialProofItem {\n  imageSrc: string;\n  name: string;\n}\n```\n</PropsReference>"
            },
            "Stats": {
              "description": "The Stats Section is a versatile component for showcasing key metrics, accomplishments, or any numerical data in an elegant grid layout. It's designed to highlight important statistics with clear visual hierarchy, making them stand out on your landing page.",
              "api": "<Usage>\nImport the component:\n\n```jsx\nimport { LandingStatsSection } from '@/components/landing/stats/LandingStatsSection';\n```\n\nBasic implementation:\n\n```jsx\n<LandingStatsSection\n  stats={[\n    { value: '150+', label: 'apps', description: 'Over 150 apps successfully delivered.' },\n    { value: '9', label: 'members', description: 'Our core team consists of 9 experts.' },\n    { value: '300+', label: 'devs', description: 'We collaborate with 300+ creative devs.' }\n  ]}\n/>\n```\n</Usage>\n\n<Examples>\n### With Title and Description\n\n\n\n```jsx\nimport { LandingStatsSection } from '@/components/landing/stats/LandingStatsSection';\n\n<LandingStatsSection\n  title=\"Our Achievements\"\n  description=\"We take pride in our accomplishments and the impact we've made across various apps and industries.\"\n  columnsDesktop={3}\n  hasBorders={true}\n  stats={[\n    { value: '150+', label: 'apps', description: 'Over 150 apps successfully delivered.' },\n    { value: '9', label: 'members', description: 'Our core team consists of 9 experts.' },\n    { value: '300+', label: 'devs', description: 'We collaborate with 300+ creative devs.' }\n  ]}\n/>\n```\n\n\n\n### Without Borders\n\n\n\n```jsx\nimport { LandingStatsSection } from '@/components/landing/stats/LandingStatsSection';\n\n<LandingStatsSection\n  columnsDesktop={3}\n  hasBorders={false}\n  stats={[\n    { value: '150+', label: 'apps', description: 'Over 150 apps successfully delivered.' },\n    { value: '9', label: 'members', description: 'Our core team consists of 9 experts.' },\n    { value: '300+', label: 'devs', description: 'We collaborate with 300+ creative devs.' }\n  ]}\n/>\n```\n\n\n\n### With Background\n\n\n\n```jsx\nimport { LandingStatsSection } from '@/components/landing/stats/LandingStatsSection';\n\n<LandingStatsSection\n  columnsDesktop={2}\n  withBackground={true}\n  variant=\"secondary\"\n  stats={[\n    { value: '150+', label: 'apps', description: 'Over 150 apps successfully delivered.' },\n    { value: '9', label: 'members', description: 'Our core team consists of 9 experts.' },\n    { value: '300+', label: 'devs', description: 'We collaborate with 300+ creative devs.' },\n    { value: '5+', label: 'awards', description: 'Recognized with 5+ awards and featured in industry publications.' }\n  ]}\n/>\n```\n\n\n\n### With Background Glow\n\n\n\n```jsx\nimport { LandingStatsSection } from '@/components/landing/stats/LandingStatsSection';\n\n<LandingStatsSection\n  columnsDesktop={2}\n  withBackground={true}\n  withBackgroundGlow={true}\n  backgroundGlowVariant=\"secondary\"\n  stats={[\n    { value: '150+', label: 'apps', description: 'Over 150 apps successfully delivered.' },\n    { value: '9', label: 'members', description: 'Our core team consists of 9 experts.' },\n    { value: '300+', label: 'devs', description: 'We collaborate with 300+ creative devs.' },\n    { value: '5+', label: 'awards', description: 'Recognized with 5+ awards and featured in industry publications.' }\n  ]}\n/>\n```\n\n\n\n### Different Column Layouts\n\nIt's possible to use different column layouts for desktop and mobile screens.\nFor example, you can use 4 columns on desktop and 1 column on mobile.\n\n\n\n```jsx\nimport { LandingStatsSection } from '@/components/landing/stats/LandingStatsSection';\n\n<LandingStatsSection\n  columnsDesktop={4}\n  columnsMobile={1}\n  stats={[\n    { value: '15+', label: 'apps', description: 'Projects delivered in the last 12 months.' },\n    { value: '9', label: 'people', description: 'That are part of our team.' },\n    { value: '30+', label: 'devs', description: 'Developers that we work with.' },\n    { value: '5+', label: 'prizes', description: 'Industry prizes since 2010.' }\n  ]}\n/>\n```\n\n\n</Examples>\n\n<PropsReference>\n### LandingStatsSection Props\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **stats** <Tippy>Array of statistic objects to display</Tippy> | `Array<{ value: string; label?: string; description: string }>` | Yes | `[]` |\n| **className** <Tippy>Additional classes to apply to the section wrapper</Tippy> | `string` | No | `''` |\n| **innerClassName** <Tippy>Additional classes to apply to the inner container</Tippy> | `string` | No | `''` |\n| **title** <Tippy>Section title text</Tippy> | `string` | No | `undefined` |\n| **titleComponent** <Tippy>Custom component to replace the default title</Tippy> | `React.ReactNode` | No | `undefined` |\n| **description** <Tippy>Section description text</Tippy> | `string \\| React.ReactNode` | No | `undefined` |\n| **descriptionComponent** <Tippy>Custom component to replace the default description</Tippy> | `React.ReactNode` | No | `undefined` |\n| **variant** <Tippy>Visual style variant of the section</Tippy> | `'primary' \\| 'secondary'` \\| `'default'` | No | `'default'` |\n| **withBackground** <Tippy>Whether to display a background color</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Whether to display a background glow effect</Tippy> | `boolean` | No | `false` |\n| **backgroundGlowVariant** <Tippy>Visual style variant of the background glow</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **columnsDesktop** <Tippy>Number of columns on desktop screens</Tippy> | `2 \\| 3 \\| 4` | No | `3` |\n| **columnsMobile** <Tippy>Number of columns on mobile screens</Tippy> | `1 \\| 2` | No | `1` |\n| **hasBorders** <Tippy>Whether to display borders between stats</Tippy> | `boolean` | No | `true` |\n| **textPosition** <Tippy>Alignment of section text</Tippy> | `'center' \\| 'left'` | No | `'center'` |\n| **children** <Tippy>Additional content to display below the stats grid</Tippy> | `React.ReactNode` | No | `undefined` |\n\n### LandingStatItem Props\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **value** <Tippy>The main statistic value to display</Tippy> | `string` | Yes | - |\n| **description** <Tippy>Description of the statistic</Tippy> | `string` | Yes | - |\n| **label** <Tippy>Optional label displayed next to the value</Tippy> | `string` | No | `undefined` |\n| **className** <Tippy>Additional classes to apply to the stat item wrapper</Tippy> | `string` | No | `''` |\n| **variant** <Tippy>Visual style variant of the stat item</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **hasBorder** <Tippy>Whether to display borders around the item</Tippy> | `boolean` | No | `true` |\n\n### Stats Object Interface\n\n```ts\ninterface StatItem {\n  value: string;\n  label?: string;\n  description: string;\n}\n```\n</PropsReference>"
            },
            "Team": {
              "description": "The Team Section component provides a beautiful and customizable way to showcase your team members on a landing page. It supports both array-based and component-based approaches, giving you flexibility in how you structure your code.",
              "api": "<Usage>\n```jsx\nimport { LandingTeamSection } from '@/components/landing/team/LandingTeamSection';\n```\n\n```jsx\n<LandingTeamSection\n  title=\"Meet Our Team\"\n  description=\"Our team is a tight-knit family of developers and visionaries, all bound by the same passion and enthusiasm.\"\n  members={[\n    { name: \"Lee Rob\", role: \"CEO & Founder\", imageSrc: \"/static/images/people/11.webp\" },\n    { name: \"David Chen\", role: \"CTO\", imageSrc: \"/static/images/people/2.webp\" },\n    { name: \"Alex Rivera\", role: \"Lead Designer\", imageSrc: \"/static/images/people/12.webp\" }\n  ]}\n/>\n```\n</Usage>\n\n<Examples>\n### Centered Text Layout\n\n\n\n```jsx\nimport { LandingTeamSection } from '@/components/landing/team/LandingTeamSection';\n\n<LandingTeamSection\n  title=\"Our Amazing Team\"\n  description=\"Meet the talented individuals behind our success.\"\n  textPosition=\"center\"\n  members={[\n    { name: \"Lee Rob\", role: \"CEO & Founder\", imageSrc: \"/static/images/people/11.webp\" },\n    { name: \"David Chen\", role: \"CTO\", imageSrc: \"/static/images/people/2.webp\" },\n    { name: \"Alex Rivera\", role: \"Lead Designer\", imageSrc: \"/static/images/people/12.webp\" }\n  ]}\n/>\n```\n\n\n\n### Secondary Variant\n\n\n\n```jsx\nimport { LandingTeamSection } from '@/components/landing/team/LandingTeamSection';\n\n<LandingTeamSection\n  title=\"Development Team\"\n  description=\"The talented engineers that build our product.\"\n  variant=\"secondary\"\n  withBackground={true}\n  members={[\n    { name: \"Lee Rob\", role: \"CEO & Founder\", imageSrc: \"/static/images/people/11.webp\" },\n    { name: \"David Chen\", role: \"CTO\", imageSrc: \"/static/images/people/2.webp\" },\n    { name: \"Alex Rivera\", role: \"Lead Designer\", imageSrc: \"/static/images/people/12.webp\" }\n  ]}\n/>\n```\n\n\n\n### Custom Team Member Styling\n\n\n\n```jsx\nimport { LandingTeamSection } from '@/components/landing/team/LandingTeamSection';\nimport { LandingTeamMember } from '@/components/landing/team/LandingTeamMember';\n\n<LandingTeamSection\n  title=\"Our Team\"\n  description=\"Meet the people who make it all happen.\"\n>\n  <LandingTeamMember\n    imageClassName=\"border-4 border-gray-200 dark:border-gray-800\"\n    member={{ name: \"Lee Rob\", role: \"CEO & Founder\", imageSrc: \"/static/images/people/11.webp\" }}\n  />\n  <LandingTeamMember\n    imageClassName=\"border-4 border-gray-200 dark:border-gray-800\"\n    member={{ name: \"David Chen\", role: \"CTO\", imageSrc: \"/static/images/people/2.webp\" }}\n  />\n  <LandingTeamMember\n    imageClassName=\"border-4 border-gray-200 dark:border-gray-800\"\n    member={{ name: \"Alex Rivera\", role: \"Lead Designer\", imageSrc: \"/static/images/people/12.webp\" }}\n  />\n</LandingTeamSection>\n```\n\n\n\n### With Background\n\n\n\n```jsx\nimport { LandingTeamSection } from '@/components/landing/team/LandingTeamSection';\n\n<LandingTeamSection\n  title=\"Meet Our Experts\"\n  description=\"The talented professionals who make everything possible.\"\n  withBackground={true}\n  members={[\n    { name: \"Lee Rob\", role: \"CEO & Founder\", imageSrc: \"/static/images/people/11.webp\" },\n    { name: \"David Chen\", role: \"CTO\", imageSrc: \"/static/images/people/2.webp\" },\n    { name: \"Alex Rivera\", role: \"Lead Designer\", imageSrc: \"/static/images/people/12.webp\" }\n  ]}\n/>\n```\n\n\n\n### With Background Glow\n\n\n\n```jsx\nimport { LandingTeamSection } from '@/components/landing/team/LandingTeamSection';\n\n<LandingTeamSection\n  title=\"Meet Our Experts\"\n  description=\"The talented professionals who make everything possible.\"\n  withBackgroundGlow={true}\n  members={[\n    { name: \"Lee Rob\", role: \"CEO & Founder\", imageSrc: \"/static/images/people/11.webp\" },\n    { name: \"David Chen\", role: \"CTO\", imageSrc: \"/static/images/people/2.webp\" },\n    { name: \"Alex Rivera\", role: \"Lead Designer\", imageSrc: \"/static/images/people/12.webp\" }\n  ]}\n/>\n```\n\n\n\n### Component-based Usage\n\nAlternatively, you can use individual `LandingTeamMember` components as children:\n\n\n\n```jsx\nimport { LandingTeamSection } from '@/components/landing/team/LandingTeamSection';\nimport { LandingTeamMember } from '@/components/landing/team/LandingTeamMember';\n\n<LandingTeamSection\n  title=\"Leadership Team\"\n  description=\"Meet the people driving our vision forward.\"\n>\n  <LandingTeamMember\n    member={{ name: \"Lee Rob\", role: \"CEO & Founder\", imageSrc: \"/static/images/people/11.webp\" }}\n  />\n  <LandingTeamMember\n    member={{ name: \"David Chen\", role: \"CTO\", imageSrc: \"/static/images/people/2.webp\" }}\n  />\n  <LandingTeamMember\n    member={{ name: \"Alex Rivera\", role: \"Lead Designer\", imageSrc: \"/static/images/people/12.webp\" }}\n  />\n</LandingTeamSection>\n```\n\n\n</Examples>\n\n<PropsReference>\n### LandingTeamSection\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **className** <Tippy>Additional CSS classes for the section container</Tippy> | `string` | No | `-` |\n| **innerClassName** <Tippy>Additional CSS classes for the inner content container</Tippy> | `string` | No | `-` |\n| **title** <Tippy>Main title text for the team section</Tippy> | `string` | No | `'Our Members'` |\n| **titleComponent** <Tippy>Custom React component to replace the default title</Tippy> | `React.ReactNode` | No | `-` |\n| **description** <Tippy>Description text shown below the title</Tippy> | `string \\| React.ReactNode` | No | `'Our team is a tight-knit family of developers and visionaries, all bound by the same passion and enthusiasm.'` |\n| **descriptionComponent** <Tippy>Custom React component to replace the default description</Tippy> | `React.ReactNode` | No | `-` |\n| **members** <Tippy>Array of team members to display</Tippy> | `TeamMember[]` | No | `[]` |\n| **textPosition** <Tippy>Alignment of the title and description text</Tippy> | `'center' \\| 'left'` | No | `'left'` |\n| **withBackground** <Tippy>Whether to display a background color</Tippy> | `boolean` | No | `false` |\n| **withBackgroundGlow** <Tippy>Whether to display a glow effect in the background</Tippy> | `boolean` | No | `false` |\n| **variant** <Tippy>Color theme variant for the component</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **backgroundGlowVariant** <Tippy>Color theme variant for the background glow effect</Tippy> | `'primary' \\| 'secondary'` | No | `'primary'` |\n| **children** <Tippy>Child components to render (used when not using the members array)</Tippy> | `React.ReactNode` | No | `-` |\n\n### LandingTeamMember\n\n| Prop Name | Prop Type | Required | Default |\n| --- | --- | ----- | ---- |\n| **className** <Tippy>Additional CSS classes for the team member container</Tippy> | `string` | No | `-` |\n| **member** <Tippy>Team member data object</Tippy> | `TeamMember` | Yes | `-` |\n| **imageClassName** <Tippy>Additional CSS classes for the team member's image</Tippy> | `string` | No | `-` |\n\n### TeamMember Interface\n\n```ts\nexport interface TeamMember {\n  name: string;\n  role: string;\n  imageSrc: string;\n}\n```\n</PropsReference>"
            },
            "TestimonialGrid": {
              "description": "Use this component to display a grid of testimonials.<br/> This component accepts a title, description and a list of testimonials. They will be placed in a column layout on small screens, then a 2-column layout and finally a 3-column layout on large screens.<br/> Each testimonial can be featured or not. The featured testimonial will stand out with bigger & bolder text. Testimonials are a great way to show that other people have used your product and are happy with it. Consider adding it high up on your landing page.",
              "api": "<Usage>\n```jsx\nimport { LandingTestimonialGrid } from '@/components/landing/testimonial/LandingTestimonialGrid';\n```\n\n```jsx\nconst testimonialItems = [\n  {\n    name: 'Mathew',\n    text: 'After using this, I cannot imagine going back to the old way of doing things.',\n    handle: '@heymatt_oo',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=2',\n  },\n  {\n    name: 'Joshua',\n    text: 'Perfect for my use case',\n    handle: '@joshua',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=3',\n  },\n  {\n    name: 'Parl Coppa',\n    text: 'This is the best thing since sliced bread. I cannot believe I did not think of it myself.',\n    handle: '@coppalipse',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=1',\n    featured: true, // Feature this testimonial\n  },\n  {\n    name: 'Mandy',\n    text: 'Excellent product!',\n    handle: '@mandy',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=4',\n  },\n  {\n    name: 'Alex',\n    text: 'Can easily recommend!',\n    handle: '@alex',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=5',\n  },\n  {\n    name: 'Sam',\n    text: 'I am very happy with the results.',\n    handle: '@sama',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=6',\n  },\n];\n```\n\n```jsx\n<LandingTestimonialGrid\n  title=\"Don't take it from us\"\n  description=\"See what other people have to say.\"\n  testimonialItems={testimonialItems}\n/>\n```\n</Usage>\n\n<Examples>\n### Background, links and features\n\nThis component supports different background colors.\n\nHere we set <b>variant</b> to <b>secondary</b>.\nTestimonials can also be linked + be featured and you can mix and match to send\nthe desired message.\n\n\n\n```jsx\nimport { LandingTestimonialGrid } from '@/components/landing/testimonial/LandingTestimonialGrid';\n\nconst testimonialItems = [\n    {\n      name: 'Mathew',\n      text: 'After using this, I cannot imagine going back to the old way of doing things.',\n      handle: '@heymatt_oo',\n      imageSrc: 'https://picsum.photos/100/100.webp?random=2',\n      featured: true, // Feature this testimonial\n    },\n    {\n      name: 'Joshua',\n      text: 'Perfect for my use case',\n      handle: '@joshua',\n      imageSrc: 'https://picsum.photos/100/100.webp?random=3',\n    },\n    {\n      name: 'Parl Coppa',\n      text: 'This is the best thing since sliced bread. I cannot believe I did not think of it myself.',\n      handle: '@coppalipse',\n      imageSrc: 'https://picsum.photos/100/100.webp?random=1',\n    },\n    {\n      name: 'Mandy',\n      text: 'Excellent product!',\n      handle: '@mandy',\n      imageSrc: 'https://picsum.photos/100/100.webp?random=4',\n      featured: true, // Feature this testimonial\n    },\n    {\n      name: 'Alex',\n      text: 'Can easily recommend this product! I am very happy with the results.',\n      handle: '@alex',\n      imageSrc: 'https://picsum.photos/100/100.webp?random=5',\n      featured: true, // Feature this testimonial\n    },\n    {\n      name: 'Sam',\n      text: 'I am very happy with the results.',\n      handle: '@sama',\n      imageSrc: 'https://picsum.photos/100/100.webp?random=6',\n    },\n  ]\n\n<LandingTestimonialGrid\n  withBackground\n  variant=\"secondary\"\n  title=\"Don't take it from us\"\n  description=\"See what other people have to say.\"\n  testimonialItems={testimonialItems}\n/>\n```\n\n\n\n### Background Glow\n\n\n\n```jsx\nimport { LandingTestimonialGrid } from '@/components/landing/testimonial/LandingTestimonialGrid';\n\nconst testimonialItems = [\n    {\n      name: 'Mathew',\n      text: 'After using this, I cannot imagine going back to the old way of doing things.',\n      handle: '@heymatt_oo',\n      imageSrc: 'https://picsum.photos/100/100.webp?random=2',\n    },\n    {\n      name: 'Joshua',\n      text: 'Perfect for my use case',\n      handle: '@joshua',\n      imageSrc: 'https://picsum.photos/100/100.webp?random=3',\n    },\n    {\n      name: 'Parl Coppa',\n      text: 'This is the best thing since sliced bread. I cannot believe I did not think of it myself.',\n      handle: '@coppalipse',\n      imageSrc: 'https://picsum.photos/100/100.webp?random=1',\n      featured: true, // Feature this testimonial\n    },\n    {\n      name: 'Mandy',\n      text: 'Excellent product!',\n      handle: '@mandy',\n      imageSrc: 'https://picsum.photos/100/100.webp?random=4',\n    },\n    {\n      name: 'Alex',\n      text: 'Can easily recommend!',\n      handle: '@alex',\n      imageSrc: 'https://picsum.photos/100/100.webp?random=5',\n    },\n    {\n      name: 'Sam',\n      text: 'I am very happy with the results.',\n      handle: '@sama',\n      imageSrc: 'https://picsum.photos/100/100.webp?random=6',\n    },\n  ]\n\n<LandingTestimonialGrid\n  title=\"Don't take it from us\"\n  description=\"See what other people have to say.\"\n  testimonialItems={testimonialItems}\n/>\n```\n\n\n\n### Read more wrapper\n\nIf your testimonials exceed 2 rows, you can add a \"Read more\" wrapper to\nhide the rest of the content initially. <br />\nThis is usually a good idea to keep the page clean and focused.\n\n\n\n```jsx\nimport { LandingTestimonialGrid } from '@/components/landing/testimonial/LandingTestimonialGrid';\nimport { LandingTestimonialReadMoreWrapper } from '@/components/landing/testimonial/LandingTestimonialReadMoreWrapper';\n\nconst testimonialItems = [\n  {\n    name: 'Mathew',\n    text: 'After using this, I cannot imagine going back to the old way of doing things.',\n    handle: '@heymatt_oo',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=2',\n  },\n  {\n    name: 'Joshua',\n    text: 'Perfect for my use case',\n    handle: '@joshua',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=3',\n  },\n  {\n    name: 'Parl Coppa',\n    text: 'This is the best thing since sliced bread. I cannot believe I did not think of it myself.',\n    handle: '@coppalipse',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=1',\n    featured: true, // Feature this testimonial\n  },\n  {\n    name: 'Mandy',\n    text: 'Excellent product!',\n    handle: '@mandy',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=4',\n  },\n  {\n    name: 'Alex',\n    text: 'Can easily recommend!',\n    handle: '@alex',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=5',\n  },\n  {\n    name: 'Sam',\n    text: 'I am very happy with the results.',\n    handle: '@sama',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=6',\n  },\n]\n\n<LandingTestimonialReadMoreWrapper size=\"md\">\n  <LandingTestimonialGrid\n    title=\"Don't take it from us\"\n    description=\"See what other people have to say.\"\n    testimonialItems={testimonialItems}\n  />\n</LandingTestimonialReadMoreWrapper>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                                      | Prop Type                    | Required | Default     |\n| ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -------- | ----------- |\n| **title** <Tippy>String or React nodes for the title of the testimonial grid.</Tippy>                                                          | `string` ǀ `React.ReactNode` | No       | -           |\n| **description** <Tippy>String or React nodes for the description of the testimonial grid.</Tippy>                                              | `string` ǀ `React.ReactNode` | No       | -           |\n| **testimonialItems** <Tippy>An array of `TestimonialItem` objects representing the testimonials to be displayed.</Tippy>                       | `Array<TestimonialItem>`     | Yes      | -           |\n| **featuredTestimonial** <Tippy>The featured testimonial to be displayed with special styling. It should be a `TestimonialItem` object.</Tippy> | `TestimonialItem`            | No       | -           |\n| **withBackground** <Tippy>A boolean indicating whether to display the testimonial grid with a background.</Tippy>                              | `boolean`                    | No       | -           |\n| **variant** <Tippy>The color variant of the background. It can be either `'primary'` or `'secondary'`.</Tippy>                                 | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n| **withBackgroundGlow** <Tippy>A boolean indicating whether to add a glowing effect to the background of the testimonial grid.</Tippy>          | `boolean`                    | No       | `false`     |\n| **backgroundGlowVariant** <Tippy>The color variant of the background glow effect. It can be either `'primary'` or `'secondary'`.</Tippy>       | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n\n```ts\nexport interface TestimonialItem {\n  className?: string;\n  url?: string;\n  text: string;\n  imageSrc: string;\n  name: string;\n  handle: string;\n  featured?: boolean;\n  verified?: boolean;\n  size?: 'full' | 'half' | 'third'; // NB: Only applies to testimonials in a list, not grid.\n}\n```\n</PropsReference>"
            },
            "TestimonialInlineItem": {
              "description": "Use this component to display a single testimonial inline. Use this to highlight short customer testimonials or reviews. are meant as short validation and are usually support for a primary or secondary Call to action. Can be used with [Testimonial Inline](/boilerplate-documentation/landing-page-components/testimonial-inline).",
              "api": "<Usage>\n```jsx\nimport { LandingTestimonialInlineItem } from '@/components/landing/testimonial/LandingTestimonialInlineItem';\n```\n\n```jsx\n<LandingTestimonialInlineItem\n  imageSrc=\"https://picsum.photos/id/65/100/100\"\n  name=\"Jane Doe\"\n  text=\"Best app ever\"\n/>\n```\n</Usage>\n\n<Examples>\n### With Testimonial Inline Wrapper\n\n\n\n```jsx\nimport { LandingTestimonialInline } from '@/components/landing/testimonial/LandingTestimonialInline';\nimport { LandingTestimonialInlineItem } from '@/components/landing/testimonial/LandingTestimonialInlineItem';\n\n<LandingTestimonialInline withBackground variant=\"secondary\">\n  <LandingTestimonialInlineItem\n    imageSrc=\"https://picsum.photos/id/64/100/100\"\n    name=\"John Doe\"\n    text=\"I love this app\"\n  />\n\n  <LandingTestimonialInlineItem\n    imageSrc=\"https://picsum.photos/id/65/100/100\"\n    name=\"Jane Doe\"\n    text=\"Best app on the market\"\n  />\n\n  <LandingTestimonialInlineItem\n    imageSrc=\"https://picsum.photos/id/669/100/100\"\n    name=\"Alice Doe\"\n    text=\"Never seen anything like it\"\n    suffix=\"CEO of Instagram\"\n  />\n\n  <LandingTestimonialInlineItem\n    imageSrc=\"https://picsum.photos/id/829/100/100\"\n    name=\"Guido Ross\"\n    text=\"Nothing comes close to it\"\n    suffix=\"DevOps at Meta\"\n  />\n</LandingTestimonialInline>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                             | Prop Type | Required | Default |\n| --------------------------------------------------------------------- | --------- | -------- | ------- |\n| **imageSrc** <Tippy>Image source URL for the avatar.</Tippy>          | `string`  | Yes      | -       |\n| **text** <Tippy>Text content of the testimonial.</Tippy>              | `string`  | Yes      | -       |\n| **name** <Tippy>Name of the person providing the testimonial.</Tippy> | `string`  | Yes      | -       |\n| **suffix** <Tippy>Optional suffix to append to the name.</Tippy>      | `string`  | No       | -       |\n</PropsReference>"
            },
            "TestimonialInline": {
              "description": "Use this to highlight short customer testimonials or reviews. These are not meant for reviews, but short validation and are usually support for a primary or secondary Call to action. It displays an inline grid of short testimonials.",
              "api": "<Usage>\n```jsx\nimport { LandingTestimonialInline } from '@/components/landing/testimonial/LandingTestimonialInline';\nimport { LandingTestimonialInlineItem } from '@/components/landing/testimonial/LandingTestimonialInlineItem';\n```\n\n```jsx\n<LandingTestimonialInline>\n  <LandingTestimonialInlineItem\n    imageSrc=\"https://picsum.photos/id/64/100/100\"\n    name=\"John Doe\"\n    text=\"I love this app\"\n  />\n\n  <LandingTestimonialInlineItem\n    imageSrc=\"https://picsum.photos/id/65/100/100\"\n    name=\"Jane Doe\"\n    text=\"Best app ever\"\n  />\n\n  <LandingTestimonialInlineItem\n    imageSrc=\"https://picsum.photos/id/669/100/100\"\n    name=\"Alice Doe\"\n    text=\"Fantastic\"\n    suffix=\"CEO of Instagram\"\n  />\n\n  <LandingTestimonialInlineItem\n    imageSrc=\"https://picsum.photos/id/829/100/100\"\n    name=\"Guido Ross\"\n    text=\"Recommended\"\n    suffix=\"DevOps at Meta\"\n  />\n</LandingTestimonialInline>\n```\n</Usage>\n\n<Examples>\n### Background Color\n\n\n\n```jsx\nimport { LandingTestimonialInline } from '@/components/landing/testimonial/LandingTestimonialInline';\nimport { LandingTestimonialInlineItem } from '@/components/landing/testimonial/LandingTestimonialInlineItem';\n\n<LandingTestimonialInline withBackground variant=\"secondary\">\n  <LandingTestimonialInlineItem\n    imageSrc=\"https://picsum.photos/id/64/100/100\"\n    name=\"John Doe\"\n    text=\"I love this app\"\n  />\n\n  <LandingTestimonialInlineItem\n    imageSrc=\"https://picsum.photos/id/65/100/100\"\n    name=\"Jane Doe\"\n    text=\"Best app on the market\"\n  />\n\n  <LandingTestimonialInlineItem\n    imageSrc=\"https://picsum.photos/id/669/100/100\"\n    name=\"Alice Doe\"\n    text=\"Never seen anything like it\"\n    suffix=\"CEO of Instagram\"\n  />\n\n  <LandingTestimonialInlineItem\n    imageSrc=\"https://picsum.photos/id/829/100/100\"\n    name=\"Guido Ross\"\n    text=\"Nothing comes close to it\"\n    suffix=\"DevOps at Meta\"\n  />\n</LandingTestimonialInline>;\n```\n\n\n\n### With Primary CTA\n\n\n\n```jsx\nimport { LandingTestimonialInline } from '@/components/landing/testimonial/LandingTestimonialInline';\nimport { LandingTestimonialInlineItem } from '@/components/landing/testimonial/LandingTestimonialInlineItem';\nimport { LandingPrimaryImageCtaSection } from '@/components/landing/cta/LandingPrimaryCta';\n\n<LandingPrimaryImageCtaSection\n  title=\"Beautiful landing pages in minutes\"\n  description=\"Get your landing page up and running with a few clicks.\"\n  imageSrc=\"/static/images/product-sample.webp\"\n  imageAlt=\"Sample image\"\n  withBackground\n  footerComponent={\n    <LandingTestimonialInline>\n      <LandingTestimonialInlineItem\n        imageSrc=\"https://picsum.photos/id/64/100/100\"\n        name=\"John Doe\"\n        text=\"I love this app\"\n      />\n\n      <LandingTestimonialInlineItem\n        imageSrc=\"https://picsum.photos/id/65/100/100\"\n        name=\"Jane Doe\"\n        text=\"Best app on the market\"\n      />\n\n      <LandingTestimonialInlineItem\n        imageSrc=\"https://picsum.photos/id/669/100/100\"\n        name=\"Alice Doe\"\n        text=\"Never seen anything like it\"\n        suffix=\"CEO of Instagram\"\n      />\n\n      <LandingTestimonialInlineItem\n        imageSrc=\"https://picsum.photos/id/829/100/100\"\n        name=\"Guido Ross\"\n        text=\"Nothing comes close to it\"\n        suffix=\"DevOps at Meta\"\n      />\n    </LandingTestimonialInline>\n  }\n>\n  <Button size=\"xl\" asChild>\n    <a href=\"#\">Sign up</a>\n  </Button>\n\n  <Button size=\"xl\" variant=\"outlinePrimary\">\n    <a href=\"#\">See demo</a>\n  </Button>\n</LandingPrimaryImageCtaSection>;\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                             | Prop Type                             | Required | Default       |\n| ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | -------- | ------------- |\n| **children** <Tippy>React nodes to be rendered within the component (below the text on smaller screen, right side on larger).</Tippy> | `React.ReactNode` ǀ `string`          | No       | -             |\n| **withBackground** <Tippy>Boolean indicating whether to display with a background or not.</Tippy>                                     | `boolean`                             | No       | `false`       |\n| **variant** <Tippy>String defining the variant of the component. Possible values are 'primary' or 'secondary'.</Tippy>                | `'primary'` ǀ `'secondary'`           | No       | `'primary'`   |\n| **containerType** <Tippy>String defining the type of container. Possible values are 'narrow', 'wide', or 'ultrawide'.</Tippy>         | `'narrow'` ǀ `'wide'` ǀ `'ultrawide'` | No       | `'ultrawide'` |\n</PropsReference>"
            },
            "TestimonialList": {
              "description": "Use this component to display a list of testimonials. Each testimonial has text, a name, and a picture of the person. Testimonials are a great way to show that other people have used your product and are happy with it. Consider adding it high up on your landing page. Shorter testimonials can be made smaller by setting the `size` prop to `half` or `third`. The default size is `full`.",
              "api": "<Usage>\n```jsx\nimport { LandingTestimonialListSection } from '@/components/landing/testimonial/LandingTestimonialList';\n```\n\n```jsx\nconst testimonialItems = [\n  {\n    name: 'Parl Coppa',\n    text: 'This is the best thing since sliced bread. I cannot believe I did not think of it myself.',\n    handle: '@coppalipse',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=1',\n    size: 'half',\n  },\n  {\n    name: 'Mathew',\n    text: 'After using this, I cannot imagine going back to the old way of doing things.',\n    handle: '@heymatt_oo',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=2',\n    size: 'half',\n  },\n  {\n    name: 'Joshua',\n    text: 'Perfect for my use case',\n    handle: '@joshua',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=3',\n    size: 'third',\n  },\n  {\n    name: 'Mandy',\n    text: 'Excellent product!',\n    handle: '@mandy',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=4',\n    size: 'third',\n  },\n  {\n    name: 'Alex',\n    text: 'Can easily recommend!',\n    handle: '@alex',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=5',\n    size: 'third',\n  },\n];\n```\n\n```jsx\n<LandingTestimonialListSection\n  title=\"Don't take it from us\"\n  description=\"See what other people have to say.\"\n  testimonialItems={testimonialItems}\n/>\n```\n</Usage>\n\n<Examples>\n### Background, links and features\n\nThis component supports different background colors.\n\nHere we set <b>variant</b> to <b>secondary</b>. <br />\nTestimonials can also be linked + be featured and you can mix and match to\nsend the desired message.\n\n\n\n```jsx\nimport { LandingTestimonialListSection } from '@/components/landing/testimonial/LandingTestimonialList';\n\nconst testimonialItems = [\n  {\n    name: 'Parl Coppa',\n    text: 'This is the best thing since sliced bread. I cannot believe I did not think of it myself.',\n    handle: '@coppalipse',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=1',\n    featured: true, // Feature this testimonial\n    url: 'https://example.com', // Link the testimonial\n  },\n  {\n    name: 'Mathew',\n    text: 'After using this, I cannot imagine going back to the old way of doing things.',\n    handle: '@heymatt_oo',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=2',\n    size: 'half',\n    url: 'https://example.com',\n  },\n  {\n    name: 'Joshua',\n    text: 'Perfect for my use case',\n    handle: '@joshua',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=3',\n    size: 'half',\n  },\n]\n\n<LandingTestimonialListSection\n  withBackground\n  variant=\"secondary\"\n  title=\"Don't take it from us\"\n  description=\"See what other people have to say.\"\n  testimonialItems={testimonialItems}\n/>\n```\n\n\n\n### With Background Glow\n\n\n\n```jsx\nimport { LandingTestimonialListSection } from '@/components/landing/testimonial/LandingTestimonialList';\n\nconst testimonialItems = [\n  {\n    name: 'Parl Coppa',\n    text: 'This is the best thing since sliced bread. I cannot believe I did not think of it myself.',\n    handle: '@coppalipse',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=1',\n    featured: true, // Feature this testimonial\n    url: 'https://example.com', // Link the testimonial\n  },\n  {\n    name: 'Mathew',\n    text: 'After using this, I cannot imagine going back to the old way of doing things.',\n    handle: '@heymatt_oo',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=2',\n    size: 'half',\n    url: 'https://example.com',\n  },\n  {\n    name: 'Joshua',\n    text: 'Perfect for my use case',\n    handle: '@joshua',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=3',\n    size: 'half',\n  },\n]\n\n<LandingTestimonialListSection\n  withBackgroundGlow\n  backgroundGlowVariant=\"secondary\"\n  title=\"Don't take it from us\"\n  description=\"See what other people have to say.\"\n  testimonialItems={testimonialItems}\n/>\n```\n\n\n\n### With Read More Wrapper\n\n\n\n```jsx\nimport { LandingTestimonialListSection } from '@/components/landing/testimonial/LandingTestimonialList';\nimport { LandingTestimonialReadMoreWrapper } from '@/components/landing/testimonial/LandingTestimonialReadMoreWrapper';\n\nconst testimonialItems = [\n  {\n    name: 'Parl Coppa',\n    text: 'This is the best thing since sliced bread. I cannot believe I did not think of it myself.',\n    handle: '@coppalipse',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=1',\n    featured: true, // Feature this testimonial\n    url: 'https://example.com', // Link the testimonial\n  },\n  {\n    name: 'Mathew',\n    text: 'After using this, I cannot imagine going back to the old way of doing things.',\n    handle: '@heymatt_oo',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=2',\n    size: 'half',\n    url: 'https://example.com',\n  },\n  {\n    name: 'Joshua',\n    text: 'Perfect for my use case',\n    handle: '@joshua',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=3',\n    size: 'half',\n  },\n]\n\n<LandingTestimonialReadMoreWrapper size=\"md\">\n  <LandingTestimonialListSection\n    withBackgroundGlow\n    backgroundGlowVariant=\"secondary\"\n    title=\"Don't take it from us\"\n    description=\"See what other people have to say.\"\n    testimonialItems={testimonialItems}\n  />\n</LandingTestimonialReadMoreWrapper>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                    | Prop Type                    | Required | Default     |\n| ------------------------------------------------------------------------------------------------------------ | ---------------------------- | -------- | ----------- |\n| **title** <Tippy>React nodes or string for the section title.</Tippy>                                        | `string` ǀ `React.ReactNode` | No       | -           |\n| **titleComponent** <Tippy>Custom React component for the section title.</Tippy>                              | `React.ReactNode`            | No       | -           |\n| **description** <Tippy>React nodes or string for the section description.</Tippy>                            | `string` ǀ `React.ReactNode` | No       | -           |\n| **descriptionComponent** <Tippy>Custom React component for the section description.</Tippy>                  | `React.ReactNode`            | No       | -           |\n| **testimonialItems** <Tippy>An array of objects representing testimonial items.</Tippy>                      | `TestimonialItem[]`          | Yes      | -           |\n| **withBackground** <Tippy>Boolean to determine whether to display section background or not.</Tippy>         | `boolean`                    | No       | `false`     |\n| **variant** <Tippy>String defining the variant of the section background.</Tippy>                            | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n| **withBackgroundGlow** <Tippy>Boolean to determine whether to display background glow effect or not.</Tippy> | `boolean`                    | No       | `false`     |\n| **backgroundGlowVariant** <Tippy>String defining the variant of the background glow effect.</Tippy>          | `'primary'` ǀ `'secondary'`  | No       | `'primary'` |\n\n```ts\nexport interface TestimonialItem {\n  className?: string;\n  url?: string;\n  text: string;\n  imageSrc: string;\n  name: string;\n  handle: string;\n  featured?: boolean;\n  verified?: boolean;\n  size?: 'full' | 'half' | 'third'; // NB: Only applies to testimonials in a list, not grid.\n}\n```\n</PropsReference>"
            },
            "Testimonial": {
              "description": "Use this component to display a single testimonial. It has text, a name, and a picture of the person. This is used as part of [Testimonial Lists](/boilerplate-documentation/landing-page-components/testimonial-list) and [Testimonial Grids](/boilerplate-documentation/landing-page-components/testimonial-grid).",
              "api": "<Usage>\n```jsx\nimport { LandingTestimonial } from '@/components/landing/testimonial/LandingTestimonial';\n```\n\n```\n<LandingTestimonial\n  name={'Mandy'}\n  text={'Excellent product. I love it!'}\n  handle={'@mandy'}\n  imageSrc={'https://picsum.photos/100/100.webp?random=3'}\n  hideFooter\n/>\n```\n</Usage>\n\n<Examples>\n### With Testimonial Grid\n\n\n\n```jsx\nimport { LandingTestimonialGrid } from '@/components/landing/testimonial/LandingTestimonialGrid';\n\nconst testimonialItems = [\n  {\n    name: 'Mathew',\n    text: 'After using this, I cannot imagine going back to the old way of doing things.',\n    handle: '@heymatt_oo',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=2',\n  },\n  {\n    name: 'Joshua',\n    text: 'Perfect for my use case',\n    handle: '@joshua',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=3',\n  },\n  {\n    name: 'Parl Coppa',\n    text: 'This is the best thing since sliced bread. I cannot believe I did not think of it myself.',\n    handle: '@coppalipse',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=1',\n    featured: true, // Feature this testimonial\n  },\n  {\n    name: 'Mandy',\n    text: 'Excellent product!',\n    handle: '@mandy',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=4',\n  },\n  {\n    name: 'Alex',\n    text: 'Can easily recommend!',\n    handle: '@alex',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=5',\n  },\n  {\n    name: 'Sam',\n    text: 'I am very happy with the results.',\n    handle: '@sama',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=6',\n  },\n];\n\n<LandingTestimonialGrid\n  title=\"Don't take it from us\"\n  description=\"See what other people have to say.\"\n  testimonialItems={testimonialItems}\n/>;\n```\n\n\n\n### With Testimonial List\n\n\n\n```jsx\nimport { LandingTestimonialListSection } from '@/components/landing/testimonial/LandingTestimonialList';\n\nconst testimonialItems = [\n  {\n    name: 'Parl Coppa',\n    text: 'This is the best thing since sliced bread. I cannot believe I did not think of it myself.',\n    handle: '@coppalipse',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=1',\n    size: 'half',\n  },\n  {\n    name: 'Mathew',\n    text: 'After using this, I cannot imagine going back to the old way of doing things.',\n    handle: '@heymatt_oo',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=2',\n    size: 'half',\n  },\n  {\n    name: 'Joshua',\n    text: 'Perfect for my use case',\n    handle: '@joshua',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=3',\n    size: 'third',\n  },\n  {\n    name: 'Mandy',\n    text: 'Excellent product!',\n    handle: '@mandy',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=4',\n    size: 'third',\n  },\n  {\n    name: 'Alex',\n    text: 'Can easily recommend!',\n    handle: '@alex',\n    imageSrc: 'https://picsum.photos/100/100.webp?random=5',\n    size: 'third',\n  },\n]\n\n<LandingTestimonialListSection\n  title=\"Don't take it from us\"\n  description=\"See what other people have to say.\"\n  testimonialItems={testimonialItems}\n/>\n```\n\n\n</Examples>\n\n<PropsReference>\n| Prop Name                                                                                                                               | Prop Type                       | Required | Default |\n| --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | -------- | ------- |\n| **url** <Tippy>The URL to navigate to when the testimonial is clicked.</Tippy>                                                          | `string`                        | Yes      | -       |\n| **text** <Tippy>The main text content of the testimonial.</Tippy>                                                                       | `string`                        | Yes      | -       |\n| **imageSrc** <Tippy>The URL of the image to be displayed alongside the testimonial.</Tippy>                                             | `string`                        | Yes      | -       |\n| **name** <Tippy>The name of the person providing the testimonial.</Tippy>                                                               | `string`                        | Yes      | -       |\n| **handle** <Tippy>The handle or username associated with the person providing the testimonial.</Tippy>                                  | `string`                        | Yes      | -       |\n| **featured** <Tippy>Whether the testimonial is featured or not.</Tippy>                                                                 | `boolean`                       | No       | -       |\n| **verified** <Tippy>Whether the testimonial is verified or not.</Tippy>                                                                 | `boolean`                       | No       | `true`  |\n| **size** <Tippy>The size of the testimonial (`full`, `half`, or `third`). NB: Only applies to testimonials in a list, not grid.</Tippy> | `'full'   ǀ 'half'   ǀ 'third'` | No       | -       |\n</PropsReference>"
            }
          }
        }
      },
      {
        "type": "file-structure",
        "description": "Define the file structure for the project",
        "prompt": "Output component code in XML format as follows:\\n<ComponentArtifact name=\"ComponentName\">\\n  <ComponentFile fileName=\"App.tsx\" isEntryFile=\"true\">\\n  {{code}}  \\n  </ComponentFile>\\n\\n </ComponentArtifact>"
      },
      {
        "type": "attention-rules",
        "description": "Attention rules for the code generator",
        "prompt": "- Please mock all image resources from picsum \n - Please mock all vedios resources from https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ \n - If the icon cannot be found in lucide-react, use svg to generate it \n - If the LandingHeader component is used, the custom logo (logoComponent) and ThemeSwitch must be configured according to the requirements \n - The width of the outermost container node needs to be \"w-full\" \n - If the LandingFooter component exists, the custom logo (logoComponent) must be configured \n - If the LandingSocialProofBand component exists, it needs to be placed at the first element of the page (and before LandingHeader)"
      }
    ]
  }
]
LLM Config

列出当前项目支持的 AI 提供商及其可选模型的信息,用于在应用中动态选择和调用模型服务。

data/config.json

{
  "providers": [
    {
      "provider": "openai",
      "models": [
        {
          "model": "anthropic/claude-3.7-sonnet",
          "title": "Claude 3.7 Sonnet (OpenRouter)",
          "baseURL": "https://openrouter.ai/api/v1",
          "features": ["vision"],
          "apiKey": "sk-****"
        },
        {
          "model": "anthropic/claude-3.5-sonnet",
          "title": "Claude 3.5 Sonnet (OpenRouter)",
          "baseURL": "https://openrouter.ai/api/v1",
          "features": ["vision"],
          "apiKey": "sk-****"
        },
        {
          "model": "openai/gpt-4o",
          "title": "GPT-4o (OpenRouter)",
          "baseURL": "https://openrouter.ai/api/v1",
          "features": ["vision"],
          "apiKey": "sk-****"
        },
        {
          "model": "gpt-4o",
          "title": "GPT-4o (302)",
          "baseURL": "https://api.302.ai/v1",
          "features": ["vision"],
          "apiKey": "sk-bjQAKhx0Wbnr5kBLjK0fKslteYMD7wsjzhsJlvXCsSa55CK8"
        },
        {
          "model": "kimi-k2-0711-preview",
          "title": "Kimi K2-0711-Preview (Moonshot)",
          "baseURL": "https://api.moonshot.cn/v1",
          "apiKey": "sk-FrrxvIT8YkBq4udSMShsXw5S55ErEcMyid4tzc5MkqGeT5G0",
          "features": ["vision"]
        }
      ]
    },
    {
      "provider": "anthropic",
      "models": [
        {
          "model": "claude-3-7-sonnet-latest",
          "title": "Claude 3.7 Sonnet (302)",
          "baseURL": "https://api.302.ai/v1",
          "features": ["vision"],
          "apiKey": "sk-bjQAKhx0Wbnr5kBLjK0fKslteYMD7wsjzhsJlvXCsSa55CK8"
        },
        {
          "model": "claude-3-5-sonnet-latest",
          "title": "Claude 3.5 Sonnet (302)",
          "baseURL": "https://api.302.ai/v1",
          "features": ["vision"],
          "apiKey": "sk-bjQAKhx0Wbnr5kBLjK0fKslteYMD7wsjzhsJlvXCsSa55CK8"
        }
      ]
    },
    {
      "provider": "deepseek",
      "models": [
        {
          "model": "deepseek-chat",
          "title": "DeepSeek-V3 Chat (DeepSeek)",
          "baseURL": "https://api.deepseek.com/v1",
          "features": [],
          "apiKey": "sk-****"
        }
      ]
    },
    {
      "provider": "ollama",
      "models": [
        {
          "model": "qwen2.5:14b",
          "title": "qwen2.5 (Ollama)",
          "baseURL": "http://localhost:11434/api",
          "features": ["vision"],
          "apiKey": ""
        }
      ]
    },
    {
      "provider": "openrouter",
      "models": [
        {
          "model": "moonshotai/kimi-k2",
          "title": "Kimi K2",
          "baseURL": "https://openrouter.ai/api/v1",
          "features": ["vision"],
          "apiKey": "sk-****",
          "extraBody": {
            "provider": {
              "only": ["Together"]
            }
          }
        },
        {
          "model": "anthropic/claude-sonnet-4",
          "title": "Claude 4 Sonnet",
          "baseURL": "https://openrouter.ai/api/v1",
          "features": ["vision"],
          "apiKey": "sk-****",
          "extraBody": {
            "provider": {
              "only": ["Anthropic"]
            }
          }
        }
      ]
    }
  ]
}
Docker 配置

Dockerfile

# ============================================================
# 阶段一:依赖安装 (deps)
# 使用 Node 18.17 Alpine 镜像,仅负责安装项目依赖
# ============================================================
FROM node:18.17-alpine AS deps
WORKDIR /app

# 构建参数:是否使用国内镜像代理(传非空值则启用 npmmirror)
ARG proxy

# 安装 libc6-compat 以兼容部分原生模块,并全局安装 pnpm 9.8.0
RUN apk add --no-cache libc6-compat && npm install -g pnpm@9.8.0

# 若指定了 proxy,则配置 pnpm 使用 npmmirror 国内镜像源
RUN [ -z "$proxy" ] || pnpm config set registry https://registry.npmmirror.com/

# 复制依赖声明文件,支持无 lock 文件时的占位
COPY package.json pnpm-lock.yaml* ./

# 必须存在 lock 文件,否则构建失败(保证依赖可复现)
RUN [ -f pnpm-lock.yaml ] || (echo "Lockfile not found." && exit 1)

# 严格按 lock 文件安装依赖,不更新版本
RUN pnpm install --frozen-lockfile

# ============================================================
# 阶段二:构建 (builder)
# 基于已安装的依赖,复制源码并执行 Next.js 构建
# ============================================================
FROM node:18.17-alpine AS builder
WORKDIR /app

ARG proxy

RUN apk add --no-cache libc6-compat && npm install -g pnpm@9.8.0

# 从 deps 阶段复制已安装的 node_modules,避免重复安装
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# 执行 Next.js 生产构建(输出到 .next)
RUN pnpm build

# ============================================================
# 阶段三:运行 (runner)
# 最小化运行镜像,仅包含运行时所需文件
# ============================================================
FROM node:18.17-alpine AS runner
WORKDIR /app

ARG proxy

# 创建 nodejs 组和 nextjs 用户,用于非 root 运行(安全最佳实践)
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# 安装 curl(健康检查用)和 CA 证书(HTTPS 请求用)
RUN apk add --no-cache curl ca-certificates \
  && update-ca-certificates

# 从 builder 阶段复制构建产物:静态资源、配置、standalone 输出、静态文件
COPY --from=builder /app/public ./public
COPY --from=builder /app/next.config.mjs ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

COPY --from=builder /app/package.json ./package.json

# 复制数据目录(如配置、模板等)
COPY --from=builder /app/data ./data

# 生产环境变量
ENV NODE_ENV production
# 关闭 Next.js 遥测
ENV NEXT_TELEMETRY_DISABLED 1
ENV PORT 3000

EXPOSE 3000

# 切换到非 root 用户运行
USER nextjs

# 健康检查:每 30 秒检查一次,启动后 30 秒内不判失败,超时 10 秒,重试 3 次
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
  CMD curl -f http://localhost:3000/ || exit 1

# Next.js standalone 模式入口
CMD ["node", "server.js"]

docker-compose.yml

# 非 host 版本, 不使用本机代理
# Docker Compose 文件格式版本
version: "3.3"

services:
  # MongoDB 数据库服务,用于 Compoder 应用数据持久化
  mini-compoder-mongo:
    # 使用官方 MongoDB 5.0.18 镜像
    image: mongo:5.0.18
    # 容器名称,便于识别与管理
    container_name: mini-compoder-mongo
    # 容器异常退出时自动重启
    restart: always
    # 端口映射:宿主机 27017 -> 容器 27017(MongoDB 默认端口)
    ports:
      - 27017:27017
    # 接入 mini-compoder 网络,便于与其他服务通信
    networks:
      - mini-compoder
    # MongoDB 根用户认证(生产环境请修改为强密码)
    environment:
      - MONGO_INITDB_ROOT_USERNAME=username
      - MONGO_INITDB_ROOT_PASSWORD=password
    # 数据卷:将数据库文件持久化到宿主机 ./database/data 目录
    volumes:
      - ./database/data:/data/db

# 自定义网络定义,供多个服务共享
networks:
  mini-compoder:

AI 驱动数据库模块实现

Cursor Project Rules

.cursor/rules/generate-schema-from-types.mdc

---
description: compoder generate:db
globs:
alwaysApply: false
---
# Role: MongoDB Schema Generator Expert

## Goals

- Analyze TypeScript type definitions to create corresponding MongoDB schema files
- Ensure proper validation and schema structure follows best practices
- Generate schema files that correctly implement all the type constraints

## Constraints

- The generated schema must properly handle all TypeScript types (primitive types, arrays, objects)
- Schema must include proper validation based on the type definitions
- Follow the existing project conventions for schema files
- Maintain correct imports and exports
- Handle special MongoDB types (ObjectId, etc.) appropriately
- Implement proper timestamps and other schema options

## Workflows

Step 1: Analyze the provided TypeScript type definitions in the types.ts file, identifying:

- Interfaces and types
- Nested structures
- Optional vs required fields
- References to other models
- Special types (ObjectId, etc.)

Step 2: Create a corresponding schema.ts file that:

- Imports mongoose and necessary types from the types.ts file
- Defines Mongoose schemas for each type/interface
- Implements proper validation for each field
- Sets appropriate required fields based on the type definitions
- Handles nested types as sub-schemas
- Sets up timestamps and other schema options

Step 3: Configure proper exports for the schema models:

- Use consistent naming conventions (e.g., TypeName  TypeNameModel)
- Handle mongoose.models checks to prevent model recompilation errors
- Export the model with proper typing

## Examples

Given a types.ts file like:

```typescript
import { Types } from "mongoose"

export interface User {
  _id: Types.ObjectId
  name: string
  email: string
  age?: number
  roles: string[]
}
```

Generate a schema.ts like:

```typescript
import mongoose, { Schema, model } from "mongoose"
import { User } from "./types"

const UserSchema = new Schema(
  {
    name: {
      type: String,
      required: true,
    },
    email: {
      type: String,
      required: true,
    },
    age: {
      type: Number,
      required: false,
    },
    roles: {
      type: [String],
      required: true,
      default: [],
    },
  },
  {
    timestamps: true,
  },
)

export const UserModel = mongoose.models.User || model<User>("User", UserSchema)
```

## Initialization

As a MongoDB Schema Generator Expert, I will analyze your TypeScript type definitions and generate corresponding Mongoose schema files that properly implement all the type constraints. I'll ensure the schema includes proper validation, follows project conventions, and handles special MongoDB types appropriately.

When you provide a @types.ts file, I'll generate the corresponding @schema.ts following the principles and patterns shown in the examples.

翻译为中文:

---
description: compoder generate:db
globs:
alwaysApply: false
---
# 角色:MongoDB Schema 生成专家

## 目标

- 分析 TypeScript 类型定义,生成对应的 MongoDB schema 文件
- 确保校验逻辑和 schema 结构符合最佳实践
- 生成的 schema 文件需正确实现所有类型约束

## 约束条件

- 生成的 schema 必须正确处理所有 TypeScript 类型(原始类型、数组、对象)
- Schema 必须根据类型定义包含适当的校验逻辑
- 遵循项目中既有的 schema 文件规范
- 保持正确的 import  export
- 妥善处理特殊 MongoDB 类型(如 ObjectId 等)
- 正确实现 timestamps 及其他 schema 配置项

## 工作流程

**步骤 1**:分析 `types.ts` 中的 TypeScript 类型定义,识别以下内容:

- 接口与类型定义
- 嵌套结构
- 可选字段与必填字段
- 对其他模型的引用
- 特殊类型(如 ObjectId 等)

**步骤 2**:创建对应的 `schema.ts` 文件,需要完成:

-  `types.ts` 中导入 mongoose 及所需类型
- 为每个类型/接口定义 Mongoose schema
- 为每个字段实现适当的校验
- 根据类型定义设置合适的 required 字段
- 将嵌套类型处理为 sub-schema
- 配置 timestamps 及其他 schema 选项

**步骤 3**:正确配置 schema 模型的导出:

- 使用一致的命名规范(如:TypeName  TypeNameModel
- 使用 `mongoose.models` 检查,避免模型重复编译错误
- 使用正确的类型导出模型

## 示例

**给定如下 `types.ts` 文件:**

```typescript
import { Types } from "mongoose"

export interface User {
  _id: Types.ObjectId
  name: string
  email: string
  age?: number
  roles: string[]
}
```

**应生成如下 `schema.ts`:**

```typescript
import mongoose, { Schema, model } from "mongoose";
import { User } from "./types";

const UserSchema = new Schema(
  {
    name: {
      type: String,
      required: true,
    },
    email: {
      type: String,
      required: true,
    },
    age: {
      type: Number,
      required: false,
    },
    roles: {
      type: [String],
      required: true,
      default: [],
    },
  },
  {
    timestamps: true,
  },
);

export const UserModel =
  mongoose.models.User || model<User>("User", UserSchema);
```

## 初始化说明

作为 MongoDB Schema 生成专家,我将分析你提供的 TypeScript 类型定义,并生成能正确实现所有类型约束的 Mongoose schema 文件。我会确保 schema 包含适当的校验、遵循项目规范,并正确处理特殊的 MongoDB 类型。

当你提供 `@types.ts` 文件时,我将按照上述示例中的原则和模式生成对应的 `@schema.ts`。

DB Schema 实现

Codegen Schema

1、数据字段类型确定

lib/db/codegen/types.ts

export interface CodegenRule {
  type:
    | "public-components"
    | "styles"
    | "private-components"
    | "file-structure"
    | "attention-rules";
  description: string;
  prompt?: string; // only used when type is "styles" | "file-structure" | "special-attention"
  dataSet?: string[]; // only used when type is "public-components"
  docs?: {
    // only used when type is "private-components"
    [libraryName: string]: {
      [componentName: string]: {
        description: string;
        api: string;
      };
    };
  };
}

export interface Codegen {
  title: string;
  description: string;
  fullStack: "React" | "Vue";
  guides: string[];
  model: string;
  codeRendererUrl: string;
  rules: CodegenRule[];
}

2、打开 Cursor Agent,选择代码能力最强的模型,输入:

compoder generate:db @lib/db/codegen/types.ts

3、检查生成的 schema.ts

lib/db/codegen/schema.ts

import mongoose from "mongoose";
import { Codegen } from "./types";

const CodegenRuleSchema = new mongoose.Schema({
  type: {
    type: String,
    enum: [
      "public-components",
      "styles",
      "private-components",
      "file-structure",
      "attention-rules",
    ],
    required: true,
  },
  description: {
    type: String,
    default: "",
    required: true,
  },
  dataSet: {
    type: [String],
    default: undefined,
  },
  prompt: {
    type: String,
    default: undefined,
  },
  docs: {
    // only used when type is "private-components"
    type: Object,
    of: {
      type: Object,
      of: {
        description: String,
        api: String,
      },
    },
    default: undefined,
  },
});

const CodegenSchema = new mongoose.Schema<Codegen>(
  {
    title: {
      type: String,
      required: true,
    },
    description: {
      type: String,
      required: true,
    },
    fullStack: {
      type: String,
      enum: ["React", "Vue"],
      required: true,
    },
    guides: {
      type: [String],
      default: [],
      required: true,
    },
    model: {
      type: String,
      required: true,
    },
    codeRendererUrl: {
      type: String,
      required: true,
    },
    rules: {
      type: [CodegenRuleSchema],
      default: [],
      required: true,
    },
  },
  {
    timestamps: true,
  },
);

export const CodegenModel =
  mongoose.models.Codegen || mongoose.model<Codegen>("Codegen", CodegenSchema);
ComponentCode Schema

1、数据字段类型确定

lib/db/componentCode/types.ts

import { Types } from "mongoose";

export type PromptText = {
  type: "text";
  text: string;
};

export type PromptImage = {
  type: "image";
  image: string;
};

export type Prompt = PromptText | PromptImage;

export type Version = {
  _id: Types.ObjectId;
  code: string;
  prompt: Prompt[];
};

export interface ComponentCode {
  _id: Types.ObjectId;
  userId: Types.ObjectId;
  codegenId: Types.ObjectId;
  name: string;
  description: string;
  versions: Version[];
}

2、打开 Cursor Agent,选择代码能力最强的模型,输入:

compoder generate:db @lib/db/componentCode/types.ts

3、检查生成的 schema.ts

lib/db/componentCode/schema.ts

import mongoose, { Schema, model } from "mongoose";
import { ComponentCode, Prompt } from "./types";

const PromptSchema = new Schema({
  type: {
    type: String,
    enum: ["text", "image"],
    required: true,
  },
  text: {
    type: String,
    required: function (this: Prompt) {
      return this.type === "text";
    },
  },
  image: {
    type: String,
    required: function (this: Prompt) {
      return this.type === "image";
    },
  },
});

const VersionSchema = new Schema({
  code: {
    type: String,
    required: true,
  },
  prompt: {
    type: [PromptSchema],
    required: true,
    validate: {
      validator: function (v: unknown) {
        return (
          Array.isArray(v) ||
          (v &&
            typeof v === "object" &&
            "type" in v &&
            ["text", "image"].includes((v as Prompt).type))
        );
      },
      message:
        "Prompt must be either a single prompt object or an array of prompts",
    },
  },
});

const ComponentCodeSchema = new Schema(
  {
    userId: {
      type: Schema.Types.ObjectId,
      required: true,
      ref: "User",
    },
    codegenId: {
      type: Schema.Types.ObjectId,
      required: true,
      ref: "Codegen",
    },
    name: {
      type: String,
      required: true,
    },
    description: {
      type: String,
      required: true,
    },
    versions: {
      type: [VersionSchema],
      required: true,
      default: [],
    },
  },
  {
    timestamps: true,
  },
);

export const ComponentCodeModel =
  mongoose.models.ComponentCode ||
  model<ComponentCode>("ComponentCode", ComponentCodeSchema);

AI 驱动后端模块实现

Cursor Project Rules

.cursor/rules/generate-sql-api.mdc

---
description: compoder generate:sql-api
globs:
alwaysApply: false
---
# MongoDB API Generation Guide

This guide is for generating API endpoints based on MongoDB.

## User Input

Please provide the following information:

1. MongoDB Schema definition, including:
   - Data structure and its types
   - Required fields
   - Default values
   - Validation rules

For example:

```typescript
// In lib/db/[modelName]/schema.ts
import mongoose, { Schema, model } from "mongoose"
import { DataType, ItemType } from "./types"

// Subdocument Schema
const ItemSchema = new Schema({
  value: {
    type: String,
    required: true,
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
})

// Main document Schema
const DataSchema = new Schema(
  {
    field1: {
      type: String,
      required: true,
    },
    field2: {
      type: String,
      required: true,
    },
    items: {
      type: [ItemSchema],
      required: true,
      default: [],
    },
  },
  {
    timestamps: true,
  },
)

// Export Model
export const Model = mongoose.models.Data || model<DataType>("Data", DataSchema)

// Define corresponding types in types.ts
export interface ItemType {
  _id: mongoose.Types.ObjectId
  value: string
  createdAt: Date
}

export interface DataType {
  _id: mongoose.Types.ObjectId
  field1: string
  field2: string
  items: ItemType[]
  createdAt: Date
  updatedAt: Date
}
```

2. API request type definition, including:
   - Request method (GET/POST/PUT/DELETE)
   - Request parameters and their types
   - Response data structure (optional)

For example:

```typescript
// In app/api/[modelName]/type.d.ts
declare namespace ApiNamespace {
  export interface RequestType {
    param1: string // Parameter 1 description
    param2: string // Parameter 2 description
    param3: string // Parameter 3 description
  }
}
```

## Generation Steps

1. Implement database operations (query or mutation) based on user input type definitions and MongoDB Schema
2. Create corresponding API route handlers

## Detailed Steps

### 1. Implement Database Operations

Based on the API request method, implement corresponding operations in the same directory level as the Schema file:

#### Query Operations (lib/db/[modelName]/selectors.ts)

```typescript
import { Model } from "./schema"
import { FilterQuery } from "mongoose"
import { DataType } from "./types"

export async function queryOperation({
  param1,
  param2,
  page,
  pageSize,
}: {
  param1: string
  param2: string
  page: number
  pageSize: number
}) {
  try {
    const skip = (page - 1) * pageSize

    // 1. Build query conditions
    let searchQuery: FilterQuery<DataType> = {
      field1: param1,
    }

    if (param2) {
      searchQuery.field2 = {
        $regex: param2,
        $options: "i",
      }
    }

    // 2. Execute query
    const [data, total] = await Promise.all([
      Model.find(searchQuery)
        .select("field1 field2 field3")
        .sort({ createdAt: -1 })
        .skip(skip)
        .limit(pageSize)
        .lean(),
      Model.countDocuments(searchQuery),
    ])

    // 3. Process return data
    const formattedData = data.map(item => ({
      id: item._id,
      value: item.field1,
      extra: item.field2,
    }))

    return {
      data: formattedData,
      total,
    }
  } catch (error) {
    console.error("Error in query operation:", error)
    throw error
  }
}
```

#### Mutation Operations (lib/db/[modelName]/mutations.ts)

```typescript
import { Model } from "./schema"

export async function databaseOperation({
  param1,
  param2,
  param3,
}: {
  param1: string
  param2: string
  param3: string
}) {
  try {
    // 1. Find record
    const record = await Model.findById(param1)
    if (!record) {
      throw new Error("Record not found")
    }

    // 2. Execute update operation
    const targetIndex = record.items.findIndex(
      (item: ItemType) => item._id.toString() === param2,
    )
    if (targetIndex === -1) {
      throw new Error("Target not found")
    }

    record.items[targetIndex].value = param3
    await record.save()

    // 3. Return result
    return {
      _id: record._id,
      ...record.toObject(),
    }
  } catch (error) {
    console.error("Error in database operation:", error)
    throw error
  }
}
```

### 2. Create API Route Handlers

In the same directory level as the API type definition, implement corresponding routes based on operation type:

#### List Query (app/api/[modelName]/list/route.ts)

```typescript
import { NextResponse } from "next/server"
import { queryOperation } from "@/lib/db/[modelName]/selectors"
import type { ApiNamespace } from "../type"
import { validateSession } from "@/lib/auth/middleware"
import { connectToDatabase } from "@/lib/db/mongo"

export async function GET(request: Request) {
  try {
    // 1. Validate session
    const authError = await validateSession()
    if (authError) {
      return authError
    }

    // 2. Connect to database
    await connectToDatabase()

    // 3. Get query parameters from URL
    const { searchParams } = new URL(request.url)
    const param1 = searchParams.get("param1")
    const param2 = searchParams.get("param2")
    const page = parseInt(searchParams.get("page") || "1")
    const pageSize = parseInt(searchParams.get("pageSize") || "10")

    if (!param1) {
      return NextResponse.json(
        { error: "Missing required parameter: param1" },
        { status: 400 },
      )
    }

    // 4. Execute query operation
    const result = await queryOperation({
      param1,
      param2,
      page,
      pageSize,
    })

    // 5. Return result
    return NextResponse.json(result)
  } catch (error) {
    console.error("Error in GET operation:", error)
    return NextResponse.json(
      { error: "Query operation failed" },
      { status: 500 },
    )
  }
}
```

#### Create Operation (app/api/[modelName]/create/route.ts)

```typescript
import { NextResponse } from "next/server"
import { createOperation } from "@/lib/db/[modelName]/mutations"
import type { ApiNamespace } from "../type"
import { validateSession } from "@/lib/auth/middleware"
import { connectToDatabase } from "@/lib/db/mongo"

export async function POST(request: Request) {
  try {
    const authError = await validateSession()
    if (authError) {
      return authError
    }

    await connectToDatabase()

    // Get data from request body
    const body = (await request.json()) as ApiNamespace.CreateRequest
    const { param1, param2, param3 } = body

    // Execute create operation
    const result = await createOperation({
      param1,
      param2,
      param3,
    })

    // Return created resource
    return NextResponse.json(result, { status: 201 })
  } catch (error) {
    console.error("Error in POST operation:", error)
    return NextResponse.json(
      { error: "Create operation failed" },
      { status: 500 },
    )
  }
}
```

#### Update Operation (app/api/[modelName]/edit/route.ts)

```typescript
import { NextResponse } from "next/server"
import { updateOperation } from "@/lib/db/[modelName]/mutations"
import type { ApiNamespace } from "../type"
import { validateSession } from "@/lib/auth/middleware"
import { connectToDatabase } from "@/lib/db/mongo"

export async function PUT(request: Request) {
  try {
    const authError = await validateSession()
    if (authError) {
      return authError
    }

    await connectToDatabase()

    // Get data from request body
    const body = (await request.json()) as ApiNamespace.EditRequest
    const { id, param1, param2 } = body

    if (!id) {
      return NextResponse.json(
        { error: "Missing required parameter: id" },
        { status: 400 },
      )
    }

    // Execute update operation
    const result = await updateOperation({
      id,
      param1,
      param2,
    })

    return NextResponse.json(result)
  } catch (error) {
    console.error("Error in PUT operation:", error)
    return NextResponse.json(
      { error: "Update operation failed" },
      { status: 500 },
    )
  }
}
```

#### Delete Operation (app/api/[modelName]/delete/route.ts)

```typescript
import { NextResponse } from "next/server"
import { deleteOperation } from "@/lib/db/[modelName]/mutations"
import type { ApiNamespace } from "../type"
import { validateSession } from "@/lib/auth/middleware"
import { connectToDatabase } from "@/lib/db/mongo"

export async function DELETE(request: Request) {
  try {
    const authError = await validateSession()
    if (authError) {
      return authError
    }

    await connectToDatabase()

    // Get parameters from URL
    const { searchParams } = new URL(request.url)
    const id = searchParams.get("id")

    if (!id) {
      return NextResponse.json(
        { error: "Missing required parameter: id" },
        { status: 400 },
      )
    }

    // Execute delete operation
    await deleteOperation({ id })

    // Return empty response, indicating successful deletion
    return new NextResponse(null, { status: 204 })
  } catch (error) {
    console.error("Error in DELETE operation:", error)
    return NextResponse.json(
      { error: "Delete operation failed" },
      { status: 500 },
    )
  }
}
```

## File Structure Example

```
lib/
  db/
    [modelName]/
      schema.ts      # MongoDB Schema definition
      types.ts       # TypeScript type definition
      selectors.ts   # Query operations
      mutations.ts   # Mutation operations
app/
  api/
    [modelName]/     # For example: componentCode
      type.d.ts      # API type definition
      create/        # Create operation
        route.ts
      detail/        # Detail operation
        route.ts
      edit/          # Edit operation
        route.ts
      list/          # List operation
        route.ts
      save/          # Save operation
        route.ts
```

## Notes

1. Type Definitions:

   - Use namespaces in `type.d.ts` to organize related types
   - Ensure type definitions are clear and complete
   - Add comments for each parameter explaining its purpose

2. Database Operations:

   - Query operations (GET):
     - Support pagination
     - Support search and filtering
     - Optimize query performance (using lean, select, etc.)
   - Mutation operations (POST/PUT/DELETE):
     - Implement appropriate error handling
     - Return unified data structure
     - Wrap all operations in try-catch

3. API Routes:
   - Must include session validation
   - Must include database connection
   - Unified error handling and response format
   - GET requests get data from URL parameters
   - POST/PUT requests get data from request body

翻译为中文:

---
description: compoder generate:sql-api
globs:
alwaysApply: false
---

# MongoDB API 生成指南

本指南用于基于 MongoDB 生成 API

## 用户输入

请提供以下信息:

1. MongoDB Schema 定义,包括:
   - 数据结构及其类型
   - 必填字段
   - 默认值
   - 验证规则

例如:

```typescript
// In lib/db/[modelName]/schema.ts
import mongoose, { Schema, model } from "mongoose"
import { DataType, ItemType } from "./types"

// Subdocument Schema
const ItemSchema = new Schema({
  value: {
    type: String,
    required: true,
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
})

// Main document Schema
const DataSchema = new Schema(
  {
    field1: {
      type: String,
      required: true,
    },
    field2: {
      type: String,
      required: true,
    },
    items: {
      type: [ItemSchema],
      required: true,
      default: [],
    },
  },
  {
    timestamps: true,
  },
)

// Export Model
export const Model = mongoose.models.Data || model<DataType>("Data", DataSchema)

// Define corresponding types in types.ts
export interface ItemType {
  _id: mongoose.Types.ObjectId
  value: string
  createdAt: Date
}

export interface DataType {
  _id: mongoose.Types.ObjectId
  field1: string
  field2: string
  items: ItemType[]
  createdAt: Date
  updatedAt: Date
}
```

2. API 请求类型定义,包括:
   - 请求方法(GET/POST/PUT/DELETE
   - 请求参数及其类型
   - 响应数据结构(可选)

例如:

```typescript
// In app/api/[modelName]/type.d.ts
declare namespace ApiNamespace {
  export interface RequestType {
    param1: string; // 参数1说明
    param2: string; // 参数2说明
    param3: string; // 参数3说明
  }
}
```

## 生成步骤

1. 根据用户输入的类型定义和 MongoDB Schema 实现数据库操作(查询或变更)
2. 创建相应的 API 路由处理器

## 详细步骤

### 1. 实现数据库操作

根据 API 请求方法,在与 Schema 文件同级的目录中实现相应的操作:

#### 查询操作 (lib/db/[modelName]/selectors.ts)

```typescript
import { Model } from "./schema";
import { FilterQuery } from "mongoose";
import { DataType } from "./types";

export async function queryOperation({
  param1,
  param2,
  page,
  pageSize,
}: {
  param1: string;
  param2: string;
  page: number;
  pageSize: number;
}) {
  try {
    const skip = (page - 1) * pageSize;

    // 1. 构建查询条件
    let searchQuery: FilterQuery<DataType> = {
      field1: param1,
    };

    if (param2) {
      searchQuery.field2 = {
        $regex: param2,
        $options: "i",
      };
    }

    // 2. 执行查询
    const [data, total] = await Promise.all([
      Model.find(searchQuery)
        .select("field1 field2 field3")
        .sort({ createdAt: -1 })
        .skip(skip)
        .limit(pageSize)
        .lean(),
      Model.countDocuments(searchQuery),
    ]);

    // 3. 处理返回数据
    const formattedData = data.map((item) => ({
      id: item._id,
      value: item.field1,
      extra: item.field2,
    }));

    return {
      data: formattedData,
      total,
    };
  } catch (error) {
    console.error("查询操作错误:", error);
    throw error;
  }
}
```

#### 变更操作 (lib/db/[modelName]/mutations.ts)

```typescript
import { Model } from "./schema";

export async function databaseOperation({
  param1,
  param2,
  param3,
}: {
  param1: string;
  param2: string;
  param3: string;
}) {
  try {
    // 1. 查找记录
    const record = await Model.findById(param1);
    if (!record) {
      throw new Error("记录未找到");
    }

    // 2. 执行更新操作
    const targetIndex = record.items.findIndex(
      (item: ItemType) => item._id.toString() === param2,
    );
    if (targetIndex === -1) {
      throw new Error("目标未找到");
    }

    record.items[targetIndex].value = param3;
    await record.save();

    // 3. 返回结果
    return {
      _id: record._id,
      ...record.toObject(),
    };
  } catch (error) {
    console.error("数据库操作错误:", error);
    throw error;
  }
}
```

### 2. 创建 API 路由处理器

在与 API 类型定义同级的目录中,根据操作类型实现相应的路由:

#### 列表查询 (app/api/[modelName]/list/route.ts)

```typescript
import { NextResponse } from "next/server";
import { queryOperation } from "@/lib/db/[modelName]/selectors";
import type { ApiNamespace } from "../type";
import { validateSession } from "@/lib/auth/middleware";
import { connectToDatabase } from "@/lib/db/mongo";

export async function GET(request: Request) {
  try {
    // 1. 验证会话
    const authError = await validateSession();
    if (authError) {
      return authError;
    }

    // 2. 连接数据库
    await connectToDatabase();

    // 3.  URL 获取查询参数
    const { searchParams } = new URL(request.url);
    const param1 = searchParams.get("param1");
    const param2 = searchParams.get("param2");
    const page = parseInt(searchParams.get("page") || "1");
    const pageSize = parseInt(searchParams.get("pageSize") || "10");

    if (!param1) {
      return NextResponse.json(
        { error: "缺少必需参数: param1" },
        { status: 400 },
      );
    }

    // 4. 执行查询操作
    const result = await queryOperation({
      param1,
      param2,
      page,
      pageSize,
    });

    // 5. 返回结果
    return NextResponse.json(result);
  } catch (error) {
    console.error("GET 操作错误:", error);
    return NextResponse.json({ error: "查询操作失败" }, { status: 500 });
  }
}
```

#### 创建操作 (app/api/[modelName]/create/route.ts)

```typescript
import { NextResponse } from "next/server";
import { createOperation } from "@/lib/db/[modelName]/mutations";
import type { ApiNamespace } from "../type";
import { validateSession } from "@/lib/auth/middleware";
import { connectToDatabase } from "@/lib/db/mongo";

export async function POST(request: Request) {
  try {
    const authError = await validateSession();
    if (authError) {
      return authError;
    }

    await connectToDatabase();

    // 从请求体获取数据
    const body = (await request.json()) as ApiNamespace.CreateRequest;
    const { param1, param2, param3 } = body;

    // 执行创建操作
    const result = await createOperation({
      param1,
      param2,
      param3,
    });

    // 返回创建的资源
    return NextResponse.json(result, { status: 201 });
  } catch (error) {
    console.error("POST 操作错误:", error);
    return NextResponse.json({ error: "创建操作失败" }, { status: 500 });
  }
}
```

#### 更新操作 (app/api/[modelName]/edit/route.ts)

```typescript
import { NextResponse } from "next/server";
import { updateOperation } from "@/lib/db/[modelName]/mutations";
import type { ApiNamespace } from "../type";
import { validateSession } from "@/lib/auth/middleware";
import { connectToDatabase } from "@/lib/db/mongo";

export async function PUT(request: Request) {
  try {
    const authError = await validateSession();
    if (authError) {
      return authError;
    }

    await connectToDatabase();

    // 从请求体获取数据
    const body = (await request.json()) as ApiNamespace.EditRequest;
    const { id, param1, param2 } = body;

    if (!id) {
      return NextResponse.json({ error: "缺少必需参数: id" }, { status: 400 });
    }

    // 执行更新操作
    const result = await updateOperation({
      id,
      param1,
      param2,
    });

    return NextResponse.json(result);
  } catch (error) {
    console.error("PUT 操作错误:", error);
    return NextResponse.json({ error: "更新操作失败" }, { status: 500 });
  }
}
```

#### 删除操作 (app/api/[modelName]/delete/route.ts)

```typescript
import { NextResponse } from "next/server";
import { deleteOperation } from "@/lib/db/[modelName]/mutations";
import type { ApiNamespace } from "../type";
import { validateSession } from "@/lib/auth/middleware";
import { connectToDatabase } from "@/lib/db/mongo";

export async function DELETE(request: Request) {
  try {
    const authError = await validateSession();
    if (authError) {
      return authError;
    }

    await connectToDatabase();

    //  URL 获取参数
    const { searchParams } = new URL(request.url);
    const id = searchParams.get("id");

    if (!id) {
      return NextResponse.json({ error: "缺少必需参数: id" }, { status: 400 });
    }

    // 执行删除操作
    await deleteOperation({ id });

    // 返回空响应,表示删除成功
    return new NextResponse(null, { status: 204 });
  } catch (error) {
    console.error("DELETE 操作错误:", error);
    return NextResponse.json({ error: "删除操作失败" }, { status: 500 });
  }
}
```

## 文件结构示例

```
lib/
  db/
    [modelName]/
      schema.ts      # MongoDB Schema 定义
      types.ts       # TypeScript 类型定义
      selectors.ts   # 查询操作
      mutations.ts   # 变更操作
app/
  api/
    [modelName]/     # 例如:componentCode
      type.d.ts      # API 类型定义
      create/        # 创建操作
        route.ts
      detail/        # 详情操作
        route.ts
      edit/          # 编辑操作
        route.ts
      list/          # 列表操作
        route.ts
      save/          # 保存操作
        route.ts
```

## 注意事项

1. 类型定义:
   -  `type.d.ts` 中使用命名空间来组织相关类型
   - 确保类型定义清晰完整
   - 为每个参数添加注释说明其用途

2. 数据库操作:
   - 查询操作(GET):
     - 支持分页
     - 支持搜索和过滤
     - 优化查询性能(使用 leanselect 等)
   - 变更操作(POST/PUT/DELETE):
     - 实现适当的错误处理
     - 返回统一的数据结构
     - 将所有操作包装在 try-catch 

3. API 路由:
   - 必须包含会话验证
   - 必须包含数据库连接
   - 统一的错误处理和响应格式
   - GET 请求从 URL 参数获取数据
   - POST/PUT 请求从请求体获取数据

DB API 实现

Codegen DB API

1、API req、res 类型确定

app/api/codegen/types.d.ts

import { Codegen } from "@/lib/db/codegen/types";

declare namespace CodegenApi {
  // codegen list request
  export interface ListRequest {
    page: number;
    pageSize: number;
    name?: string;
    fullStack?: "React" | "Vue";
  }
  // codegen list response
  export interface ListResponse {
    data: Pick<Codegen, "_id" | "title" | "description" | "fullStack">[];
    total: number;
  }
  // codegen detail request
  export interface DetailRequest {
    id: string;
  }
  // codegen detail response
  export interface DetailResponse {
    data: Pick<
      Codegen,
      | "_id"
      | "title"
      | "description"
      | "fullStack"
      | "guides"
      | "codeRendererUrl"
    >;
  }
}

2、打开 Cursor Agent,选择代码能力最强的模型,输入:

compoder generate:sql-api @lib/db/codegen/schema.ts @lib/db/codegen/types.ts @app/api/codegen/types.d.ts

3、检查生成的代码文件。

(1)、lib/db/codegen/selectors.ts

import { CodegenModel } from "./schema";
import { CodegenApi } from "@/app/api/codegen/types";
import { Codegen } from "./types";

export async function findCodegens(params: CodegenApi.ListRequest) {
  const { page, pageSize, name, fullStack } = params;

  // Build query conditions
  const query: Record<string, unknown> = {};

  if (name) {
    // Use regex for fuzzy title search
    query.title = { $regex: name, $options: "i" };
  }

  if (fullStack) {
    query.fullStack = fullStack;
  }

  // Execute query
  const skip = (page - 1) * pageSize;

  const [data, total] = await Promise.all([
    CodegenModel.find(query)
      .select("   title description fullStack")
      .skip(skip)
      .limit(pageSize)
      .lean(),
    CodegenModel.countDocuments(query),
  ]);

  return {
    data,
    total,
  };
}

export async function findCodegenById(id: string) {
  const codegen = await CodegenModel.findById(id)
    .select("_id title description fullStack guides codeRendererUrl rules")
    .lean<
      Pick<
        Codegen,
        | "title"
        | "description"
        | "fullStack"
        | "guides"
        | "codeRendererUrl"
        | "rules"
      > & {
        _id: string;
      }
    >();

  if (!codegen) {
    throw new Error("Codegen not found");
  }

  return codegen;
}

export async function getCodeRendererUrl(codegenId: string) {
  const codegen = await CodegenModel.findById(codegenId);
  return codegen?.codeRendererUrl;
}

(2)、app/api/codegen/list/route.ts

import { NextRequest, NextResponse } from "next/server";
import { findCodegens } from "@/lib/db/codegen/selectors";
import { CodegenApi } from "../types";
import { validateSession } from "@/lib/auth/middleware";
import { connectToDatabase } from "@/lib/db/mongo";

export async function GET(req: NextRequest) {
  try {
    // Add identity verification check
    const authError = await validateSession();
    if (authError) {
      return authError;
    }

    await connectToDatabase();

    const searchParams = req.nextUrl.searchParams;

    // Parse query parameters
    const params: CodegenApi.ListRequest = {
      page: parseInt(searchParams.get("page") || "1"),
      pageSize: parseInt(searchParams.get("pageSize") || "10"),
      name: searchParams.get("name") || undefined,
      fullStack:
        (searchParams.get(
          "fullStack",
        ) as CodegenApi.ListRequest["fullStack"]) || undefined,
    };

    // Validate parameters
    if (isNaN(params.page) || params.page < 1) {
      return NextResponse.json(
        { error: "Invalid page parameter" },
        { status: 400 },
      );
    }

    if (isNaN(params.pageSize) || params.pageSize < 1) {
      return NextResponse.json(
        { error: "Invalid pageSize parameter" },
        { status: 400 },
      );
    }

    // Query data
    const result = await findCodegens(params);

    return NextResponse.json(result);
  } catch (error) {
    console.error("Failed to fetch codegens:", error);
    return NextResponse.json(
      { error: "Internal Server Error" },
      { status: 500 },
    );
  }
}

export const dynamic = "force-dynamic";

(3)、app/api/codegen/detail/route.ts

import { NextRequest } from "next/server";
import { findCodegenById } from "@/lib/db/codegen/selectors";
import { CodegenApi } from "../types";
import { connectToDatabase } from "@/lib/db/mongo";
import { validateSession } from "@/lib/auth/middleware";

export async function GET(request: NextRequest) {
  try {
    // Add identity verification check
    const authError = await validateSession();
    if (authError) {
      return authError;
    }

    await connectToDatabase();

    // 从 URL 查询参数中获取 id,而不是从 body 中获取
    const searchParams = request.nextUrl.searchParams;
    const id = searchParams.get("id");

    if (!id) {
      return Response.json({ error: "Missing id parameter" }, { status: 400 });
    }

    const data = await findCodegenById(id);

    return Response.json({
      data,
    } satisfies CodegenApi.DetailResponse);
  } catch (error) {
    console.error("[CODEGEN_DETAIL]", error);
    return Response.json(
      { error: "Failed to fetch codegen detail" },
      { status: 500 },
    );
  }
}

export const dynamic = "force-dynamic";
ComponentCode DB API

1、API req、res 类型确定

app/api/componentCode/type.d.ts

import { ComponentCode } from "@/lib/db/componentCode/types";
import { Prompt } from "@/lib/db/componentCode/types";

declare namespace ComponentCodeApi {
  // list request
  export interface listRequest {
    codegenId: string;
    page: number;
    pageSize: number;
    searchKeyword?: string;
    filterField?: "all" | "name" | "description";
  }
  export interface listResponse {
    data: Array<
      Pick<ComponentCode, "_id" | "name" | "description"> & {
        latestVersionCode: string;
      }
    >;
    total: number;
  }
  // detail request
  export interface detailRequest {
    id: string;
    codegenId: string;
  }
  // detail response
  export interface detailResponse {
    data: Pick<ComponentCode, "_id" | "name" | "description" | "versions"> & {
      codeRendererUrl: string;
    };
  }

  // create request
  export interface createRequest {
    codegenId: string;
    prompt: Prompt[];
    model: string;
    provider: string;
  }

  // create response
  export type createResponse = ReadableStream;

  // edit request
  export interface editRequest {
    codegenId: string;
    prompt: Prompt[];
    component: {
      id: string;
      name: string;
      code: string;
      prompt: Prompt[];
    };
    model: string;
    provider: string;
  }

  // edit response
  export type editResponse = ReadableStream;

  // save request
  export interface saveRequest {
    id: string;
    versionId: string;
    code: string;
  }

  // delete request
  export interface deleteRequest {
    id: string;
  }
}

2、打开 Cursor Agent,选择代码能力最强的模型,输入:

compoder generate:sql-api @lib/db/componentCode/schema.ts @lib/db/componentCode/types.ts @app/api/componentCode/types.d.ts

3、检查生成的代码文件。

(1)、lib/db/componentCode/selectors.ts

import { ComponentCodeModel } from "./schema";
import { FilterQuery } from "mongoose";
import { ComponentCode } from "./types";

export async function listComponentCodes({
  userId,
  codegenId,
  page,
  pageSize,
  searchKeyword,
  filterField = "all",
}: {
  userId: string;
  codegenId: string;
  page: number;
  pageSize: number;
  searchKeyword?: string;
  filterField?: "all" | "name" | "description";
}) {
  const skip = (page - 1) * pageSize;

  let searchQuery: FilterQuery<ComponentCode> = {};

  if (searchKeyword) {
    if (filterField === "all") {
      searchQuery = {
        $or: [
          { name: { $regex: searchKeyword, $options: "i" } },
          { description: { $regex: searchKeyword, $options: "i" } },
        ],
      };
    } else {
      searchQuery = {
        [filterField]: { $regex: searchKeyword, $options: "i" },
      };
    }
  }

  // 执行查询
  const [data, total] = await Promise.all([
    ComponentCodeModel.find({ userId, codegenId, ...searchQuery })
      .select("_id name description versions")
      .sort({ createdAt: -1 })
      .skip(skip)
      .limit(pageSize)
      .lean(),
    ComponentCodeModel.countDocuments({ userId, codegenId, ...searchQuery }),
  ]);

  // 处理返回数据格式
  const formattedData = data.map((item) => ({
    _id: item._id,
    name: item.name,
    description: item.description,
    codegenId: item.codegenId,
    latestVersionCode: item.versions?.[item.versions.length - 1]?.code || "",
  }));

  return {
    data: formattedData,
    total,
  };
}

export async function getComponentCodeDetail(id: string) {
  const componentCode = await ComponentCodeModel.findById(id)
    .select("_id name description versions")
    .lean();

  if (!componentCode) {
    throw new Error("Component code not found");
  }

  return componentCode;
}

(2)、lib/db/componentCode/mutations.ts

import { ComponentCodeModel } from "./schema";
import { Prompt, Version } from "./types";

export async function createComponentCode({
  userId,
  codegenId,
  name,
  description,
  prompt,
  code,
}: {
  userId: string;
  codegenId: string;
  name: string;
  description: string;
  prompt: Prompt[];
  code: string;
}) {
  try {
    const componentCode = await ComponentCodeModel.create({
      userId,
      codegenId,
      name,
      description,
      versions: [
        {
          code,
          prompt,
        },
      ],
    });

    return {
      _id: componentCode._id,
      ...componentCode.toObject(),
    };
  } catch (error) {
    console.error("Error creating component code:", error);
    throw error;
  }
}

export async function updateComponentCode({
  id,
  prompt,
  code,
}: {
  id: string;
  prompt: Prompt[];
  code: string;
}) {
  try {
    const componentCode = await ComponentCodeModel.findById(id);
    if (!componentCode) {
      throw new Error("Component code not found");
    }
    componentCode.versions.push({ prompt, code });
    await componentCode.save();
    return {
      _id: componentCode._id,
      ...componentCode.toObject(),
    };
  } catch (error) {
    console.error("Error updating component code:", error);
    throw error;
  }
}

export async function saveComponentCodeVersion({
  id,
  versionId,
  code,
}: {
  id: string;
  versionId: string;
  code: string;
}) {
  try {
    const componentCode = await ComponentCodeModel.findById(id);
    if (!componentCode) {
      throw new Error("Component code not found");
    }

    const versionIndex = componentCode.versions.findIndex(
      (v: Version) => v._id.toString() === versionId,
    );
    if (versionIndex === -1) {
      throw new Error("Version not found");
    }

    componentCode.versions[versionIndex].code = code;
    await componentCode.save();

    return {
      _id: componentCode._id,
      ...componentCode.toObject(),
    };
  } catch (error) {
    console.error("Error saving component code version:", error);
    throw error;
  }
}

export async function deleteComponentCode({ id }: { id: string }) {
  try {
    const result = await ComponentCodeModel.findByIdAndDelete(id);
    if (!result) {
      throw new Error("Component code not found");
    }
    return { success: true };
  } catch (error) {
    console.error("Error deleting component code:", error);
    throw error;
  }
}

(3)、app/api/componentCode/create/route.ts

import { NextRequest, NextResponse } from "next/server";
import { run } from "@/app/api/ai-core/workflow";
import { ComponentCodeApi } from "../type";
import { findCodegenById } from "@/lib/db/codegen/selectors";
import { getAIClient } from "@/app/api/ai-core/utils/aiClient";
import { getUserId } from "@/lib/auth/middleware";
import { connectToDatabase } from "@/lib/db/mongo";
import { validateSession } from "@/lib/auth/middleware";
import { LanguageModel } from "ai";
import { AIProvider } from "@/lib/config/ai-providers";

export async function POST(request: NextRequest) {
  try {
    const authError = await validateSession();
    if (authError) {
      return authError;
    }

    await connectToDatabase();

    const userId = await getUserId();

    const encoder = new TextEncoder();
    const stream = new TransformStream();
    const writer = stream.writable.getWriter();

    const body = (await request.json()) as ComponentCodeApi.createRequest;
    const codegenDetail = await findCodegenById(body.codegenId);

    const aiModel = getAIClient(body.provider as AIProvider, body.model);

    const response = new Response(stream.readable);

    run({
      stream: {
        write: (chunk: string) => writer.write(encoder.encode(chunk)),
        close: () => writer.close(),
      },
      query: {
        prompt: body.prompt,
        aiModel: aiModel as LanguageModel,
        rules: codegenDetail.rules,
        userId: userId!,
        codegenId: body.codegenId,
      },
    });

    return response;
  } catch (error) {
    console.error("Failed to get component code detail:", error);
    return NextResponse.json(
      { error: "Failed to get component code detail" },
      { status: 500 },
    );
  }
}

(4)、app/api/componentCode/delete/route.ts

import { NextResponse } from "next/server";
import { deleteComponentCode } from "@/lib/db/componentCode/mutations";
import { validateSession } from "@/lib/auth/middleware";
import { connectToDatabase } from "@/lib/db/mongo";

export async function DELETE(request: Request) {
  try {
    // 1. Validate session
    const authError = await validateSession();
    if (authError) {
      return authError;
    }

    // 2. Connect to database
    await connectToDatabase();

    // 3. Get query parameters from URL
    const { searchParams } = new URL(request.url);
    const id = searchParams.get("id");

    if (!id) {
      return NextResponse.json(
        { error: "Missing required parameter: id" },
        { status: 400 },
      );
    }

    // 4. Execute delete operation
    await deleteComponentCode({ id });

    // 5. Return empty response, indicating successful deletion
    return new NextResponse(null, { status: 204 });
  } catch (error) {
    console.error("Error in DELETE operation:", error);
    return NextResponse.json(
      { error: "Delete operation failed" },
      { status: 500 },
    );
  }
}

(5)、app/api/componentCode/detail/route.ts

import { NextRequest, NextResponse } from "next/server";
import { getComponentCodeDetail } from "@/lib/db/componentCode/selectors";
import { ComponentCodeApi } from "../type";
import { ComponentCode } from "@/lib/db/componentCode/types";
import { getCodeRendererUrl } from "@/lib/db/codegen/selectors";

export async function GET(request: NextRequest) {
  try {
    const searchParams = request.nextUrl.searchParams;
    const id = searchParams.get("id");
    const codegenId = searchParams.get("codegenId");

    if (!id) {
      return NextResponse.json(
        { error: "Missing required parameter: id" },
        { status: 400 },
      );
    }

    const data = (await getComponentCodeDetail(id)) as ComponentCode;

    if (!codegenId) {
      return NextResponse.json(
        { error: "Missing required parameter: codegenId" },
        { status: 400 },
      );
    }

    const codeRendererUrl = await getCodeRendererUrl(codegenId);

    const response: ComponentCodeApi.detailResponse = {
      data: {
        _id: data._id,
        name: data.name,
        description: data.description,
        versions: data.versions,
        codeRendererUrl,
      },
    };

    return NextResponse.json(response);
  } catch (error) {
    console.error("Failed to get component code detail:", error);
    return NextResponse.json(
      { error: "Failed to get component code detail" },
      { status: 500 },
    );
  }
}

export const dynamic = "force-dynamic";

(6)、app/api/componentCode/edit/route.ts

import { NextRequest, NextResponse } from "next/server";
import { run } from "@/app/api/ai-core/workflow";
import { ComponentCodeApi } from "../type";
import { findCodegenById } from "@/lib/db/codegen/selectors";
import { getAIClient } from "@/app/api/ai-core/utils/aiClient";
import { getUserId } from "@/lib/auth/middleware";
import { connectToDatabase } from "@/lib/db/mongo";
import { validateSession } from "@/lib/auth/middleware";
import { LanguageModel } from "ai";
import { AIProvider } from "@/lib/config/ai-providers";

export async function POST(request: NextRequest) {
  try {
    const authError = await validateSession();
    if (authError) {
      return authError;
    }

    await connectToDatabase();

    const userId = await getUserId();

    const encoder = new TextEncoder();
    const stream = new TransformStream();
    const writer = stream.writable.getWriter();

    const params: ComponentCodeApi.editRequest = await request.json();

    const aiModel = getAIClient(params.provider as AIProvider, params.model);

    // validate parameters
    if (!params.codegenId || !params.prompt || !params.component) {
      return NextResponse.json(
        { error: "Missing required parameters" },
        { status: 400 },
      );
    }

    const codegenDetail = await findCodegenById(params.codegenId);

    run({
      stream: {
        write: (chunk: string) => writer.write(encoder.encode(chunk)),
        close: () => writer.close(),
      },
      query: {
        prompt: params.prompt,
        aiModel: aiModel as LanguageModel,
        rules: codegenDetail.rules,
        userId: userId!,
        component: params.component,
      },
    });

    return new Response(stream.readable);
  } catch (error) {
    console.error("Failed to get component code detail:", error);
    return NextResponse.json(
      { error: "Failed to get component code detail" },
      { status: 500 },
    );
  }
}

(7)、app/api/componentCode/list/route.ts

import { NextRequest, NextResponse } from "next/server";
import { listComponentCodes } from "@/lib/db/componentCode/selectors";
import { getUserId, validateSession } from "@/lib/auth/middleware";
import { connectToDatabase } from "@/lib/db/mongo";

export async function GET(req: NextRequest) {
  try {
    // Add identity verification check
    const authError = await validateSession();
    if (authError) {
      return authError;
    }

    await connectToDatabase();

    const userId = await getUserId();

    const searchParams = req.nextUrl.searchParams;
    const codegenId = searchParams.get("codegenId") || "";
    const page = parseInt(searchParams.get("page") || "1");
    const pageSize = parseInt(searchParams.get("pageSize") || "10");
    const searchKeyword = searchParams.get("searchKeyword") || undefined;
    const filterField =
      (searchParams.get("filterField") as "all" | "name" | "description") ||
      "all";

    // 参数验证
    if (isNaN(page) || isNaN(pageSize) || page < 1 || pageSize < 1) {
      return NextResponse.json(
        { error: "Invalid page or pageSize parameters" },
        { status: 400 },
      );
    }

    const result = await listComponentCodes({
      page,
      pageSize,
      searchKeyword,
      filterField,
      userId: userId!,
      codegenId,
    });

    return NextResponse.json(result);
  } catch (error) {
    console.error("Error in component code list API:", error);
    return NextResponse.json(
      { error: "Internal Server Error" },
      { status: 500 },
    );
  }
}

export const dynamic = "force-dynamic";

(8)、app/api/componentCode/save/route.ts

import { NextResponse } from "next/server";
import { saveComponentCodeVersion } from "@/lib/db/componentCode/mutations";
import type { ComponentCodeApi } from "../type";
import { validateSession } from "@/lib/auth/middleware";
import { connectToDatabase } from "@/lib/db/mongo";

export async function POST(request: Request) {
  try {
    const authError = await validateSession();
    if (authError) {
      return authError;
    }

    await connectToDatabase();

    const body = (await request.json()) as ComponentCodeApi.saveRequest;
    const { id, versionId, code } = body;

    const result = await saveComponentCodeVersion({
      id,
      versionId,
      code,
    });

    return NextResponse.json(result);
  } catch (error) {
    console.error("Error saving component code:", error);
    return NextResponse.json(
      { error: "Failed to save component code" },
      { status: 500 },
    );
  }
}

AI 驱动前端模块实现

AI 驱动业务组件设计与实现

Cursor Project Rules

.cursor/rules/generate-biz-component.mdc

---
description: compoder generate:biz-component
globs:
---
# Role: Frontend Business Component Development Expert

## Goals

- Clearly understand the business component requirements proposed by users.

- Generate complete business component code that complies with code standards based on user descriptions.

## Constraints

- All components used in business components must come from the `shadcn-ui` component library.

- Components must follow the data decoupling principle:
  - All data that needs to be retrieved from the server must be passed in through props; it is forbidden to initiate requests directly within the component.
  - Data source-related props must provide the following:
    - Initialization data (initialData/defaultData, etc.)
  - All operations that trigger data changes must be passed through callback function props, such as:
    - onDataChange - Data change callback
    - onSearch - Search callback
    - onPageChange - Pagination change callback
    - onFilterChange - Filter condition change callback
    - onSubmit - Form submission callback

## Workflows

Step 1: Based on the user's requirements, analyze which `shadcn-ui` components are needed to implement the requirements.

Step 2: Based on the analyzed components, generate the corresponding business component code. The standard template for business components is as follows:

Components include 4 types of files, with corresponding file names and rules as follows:

    1. index.ts (exports the component)
    This file contains the following:
    export { default as [ComponentName] } from './[ComponentName]';
    export type { [ComponentName]Props } from './interface';

    2. interface.ts
    This file contains the following, please complete the component's props content:
    interface [ComponentName]Props {}
    export type { [ComponentName]Props };

    3. [ComponentName].stories.tsx
    This file uses import type { Meta, StoryObj } from '@storybook/react' to write a storybook document for the component. You must write a complete storybook document based on the component's props, and mock data is required for each prop.

    4. [ComponentName].tsx
    This file contains the component's actual business logic and styles. Styles should be written using tailwindcss.

## Initialization

As a frontend business component development expert, you are very clear about your [Goals], while always keeping in mind the [Constraints]. You will communicate with users in clear and precise language, think step by step according to the [Workflows], and provide code generation services to users wholeheartedly.

翻译为中文:

---
description: compoder generate:biz-component
globs:
---
# 角色:前端业务组件开发专家

## 目标

- 清晰理解用户提出的业务组件需求。

- 根据用户描述,生成符合代码规范的完整业务组件代码。

## 约束

- 业务组件中使用的所有组件必须来自 `shadcn-ui` 组件库。

- 组件必须遵循数据解耦原则:
  - 所有需要从服务端获取的数据必须通过 props 传入;禁止在组件内部直接发起请求。
  - 与数据源相关的 props 必须提供以下内容:
    - 初始化数据(initialData/defaultData 等)
  - 所有会触发数据变更的操作必须通过回调函数类型的 props 传递,例如:
    - onDataChange - 数据变更回调
    - onSearch - 搜索回调
    - onPageChange - 分页变更回调
    - onFilterChange - 筛选条件变更回调
    - onSubmit - 表单提交回调

## 工作流程

第一步:根据用户需求,分析实现该需求需要用到哪些 `shadcn-ui` 组件。

第二步:根据分析出的组件,生成对应的业务组件代码。业务组件的标准模板如下:

组件包含 4 类文件,对应的文件名与规则如下:

    1. index.ts(导出组件)
    该文件包含以下内容:
    export { default as [ComponentName] } from './[ComponentName]';
    export type { [ComponentName]Props } from './interface';

    2. interface.ts
    该文件包含以下内容,请根据组件完善 props 内容:
    interface [ComponentName]Props {}
    export type { [ComponentName]Props };

    3. [ComponentName].stories.tsx
    该文件使用 import type { Meta, StoryObj } from '@storybook/react' 为组件编写 Storybook 文档。必须根据组件的 props 编写完整的 Storybook 文档,且每个 prop 都需要提供 mock 数据。

    4. [ComponentName].tsx
    该文件包含组件的实际业务逻辑与样式。样式应使用 tailwindcss 编写。

## 初始化

作为前端业务组件开发专家,你非常清楚自己的【目标】,并始终牢记【约束】。你将用清晰、准确的语言与用户沟通,按【工作流程】逐步思考,并全心全意为用户提供代码生成服务。
案例

1、让 Cursor 帮我们生成下图所示的业务组件。

2、打开 Cursor Agent,选择代码能力最强的模型,将图片复制到对话框,并输入:

compoder generate:biz-component 帮我生成图中的业务组件,代码保存到@components/biz ,组件名为:LoginFormExample

3、运行 Storybook

pnpm storybook
Prompt To Code

Compoder 研发业务组件,采取的是 Prompt To Code。

Prompt 如何写
案例

Codegen 可视化配置

export interface CodegenRule {
  type:
    | "public-components"
    | "styles"
    | "private-components"
    | "file-structure"
    | "attention-rules";
  description: string;
  prompt?: string; // only used when type is "styles" | "file-structure" | "special-attention"
  dataSet?: string[]; // only used when type is "public-components"
  docs?: {
    // only used when type is "private-components"
    [libraryName: string]: {
      [componentName: string]: {
        description: string;
        api: string;
      };
    };
  };
}

export interface Codegen {
  title: string;
  description: string;
  fullStack: "React" | "Vue";
  guides: string[];
  model: string;
  codeRendererUrl: string;
  rules: CodegenRule[];
}

1、新建 components/biz/CodegenConfig/CodegenConfig.feature,打开 Cursor Agent,选择代码能力最强的模型,并输入:

采用 DBB 中的 Gherkin 语法,描述 Codegen 可视化配置的产品需求,生成的内容保存到 @components/biz/CodegenConfig/CodegenConfig.feature @lib/db/codegen/types.ts

components/biz/CodegenConfig/CodegenConfig.feature

# language: zh-CN
功能: Codegen 可视化配置管理

  作为 系统管理员
  我想要 通过可视化界面配置和管理 Codegen 代码生成器
  以便于 快速创建和修改代码生成规则,提高开发效率

  背景:
    假设 用户已登录系统
    并且 用户具有管理员权限

  场景大纲: 创建新的 Codegen 配置
    假设 用户进入 Codegen 配置管理页面
    当 用户点击"创建新配置"按钮
    那么 显示 Codegen 配置表单

    当 用户填写以下基本信息:
      | 字段名          | 值                    |
      | title           | "<配置标题>"           |
      | description     | "<配置描述>"           |
      | fullStack       | "<技术栈>"             |
      | model           | "<AI模型名称>"         |
      | codeRendererUrl | "<代码渲染器URL>"      |
    那么 表单显示已填写的信息

    当 用户添加指南提示:
      | 指南内容                    |
      | "<指南1>"                   |
      | "<指南2>"                   |
    那么 指南列表显示已添加的指南

    当 用户添加配置规则:
      | 规则类型            | 描述              | 额外字段值        |
      | public-components   | "<规则描述>"      | dataSet: ["<组件库>"] |
      | styles              | "<规则描述>"      | prompt: "<样式提示>" |
      | private-components  | "<规则描述>"      | docs: "<文档对象>" |
      | file-structure      | "<规则描述>"      | prompt: "<文件结构提示>" |
      | attention-rules     | "<规则描述>"      | prompt: "<注意规则提示>" |
    那么 规则列表显示已添加的规则

    当 用户点击"保存"按钮
    那么 系统验证表单数据完整性
    并且 保存配置到数据库
    并且 显示成功提示消息
    并且 跳转到配置列表页面

  场景: 编辑现有 Codegen 配置
    假设 用户进入 Codegen 配置管理页面
    并且 系统中已存在一个 Codegen 配置
    当 用户点击配置列表中的"编辑"按钮
    那么 显示该配置的编辑表单
    并且 表单中预填充了现有配置数据

    当 用户修改配置的标题为 "<新标题>"
    那么 标题字段显示 "<新标题>"

    当 用户添加一条新的规则
    那么 规则列表增加一条规则

    当 用户删除一条现有规则
    那么 规则列表减少一条规则

    当 用户点击"保存"按钮
    那么 系统更新数据库中的配置
    并且 显示成功提示消息

  场景: 配置公共组件规则 (public-components)
    假设 用户正在创建或编辑 Codegen 配置
    当 用户选择添加规则类型为 "public-components"
    那么 显示规则描述输入框
    并且 显示数据集 (dataSet) 输入区域

    当 用户输入规则描述为 "定义使用的公共组件库"
    并且 用户添加数据集项 "shadcn/ui"
    并且 用户添加数据集项 "antd"
    那么 数据集列表显示 ["shadcn/ui", "antd"]

    当 用户未填写规则描述
    或者 用户未添加任何数据集项
    并且 用户尝试保存
    那么 显示验证错误提示
    并且 阻止保存操作

  场景: 配置样式规则 (styles)
    假设 用户正在创建或编辑 Codegen 配置
    当 用户选择添加规则类型为 "styles"
    那么 显示规则描述输入框
    并且 显示提示词 (prompt) 输入区域

    当 用户输入规则描述为 "定义样式生成规则"
    并且 用户输入提示词为 "使用 Tailwind CSS 编写样式,支持深色/浅色模式"
    那么 提示词输入框显示输入的文本

    当 用户未填写规则描述
    或者 用户未填写提示词
    并且 用户尝试保存
    那么 显示验证错误提示
    并且 阻止保存操作

  场景: 配置私有组件规则 (private-components)
    假设 用户正在创建或编辑 Codegen 配置
    当 用户选择添加规则类型为 "private-components"
    那么 显示规则描述输入框
    并且 显示文档 (docs) 配置区域

    当 用户输入规则描述为 "定义私有组件文档"
    并且 用户添加组件库 "my-library"
    并且 用户添加组件 "Button" 并填写描述和 API 文档
    那么 文档结构显示已配置的组件信息

    当 用户未填写规则描述
    或者 用户未配置任何组件文档
    并且 用户尝试保存
    那么 显示验证错误提示
    并且 阻止保存操作

  场景: 配置文件结构规则 (file-structure)
    假设 用户正在创建或编辑 Codegen 配置
    当 用户选择添加规则类型为 "file-structure"
    那么 显示规则描述输入框
    并且 显示提示词 (prompt) 输入区域

    当 用户输入规则描述为 "定义文件结构规范"
    并且 用户输入提示词为 "组件代码以 XML 格式输出,包含 ComponentArtifact 和 ComponentFile 标签"
    那么 提示词输入框显示输入的文本

    当 用户未填写规则描述
    或者 用户未填写提示词
    并且 用户尝试保存
    那么 显示验证错误提示
    并且 阻止保存操作

  场景: 配置注意规则 (attention-rules)
    假设 用户正在创建或编辑 Codegen 配置
    当 用户选择添加规则类型为 "attention-rules"
    那么 显示规则描述输入框
    并且 显示提示词 (prompt) 输入区域

    当 用户输入规则描述为 "定义代码生成时的注意事项"
    并且 用户输入提示词为 "仅使用以下 npm 包: react, react-dom, lucide-react"
    那么 提示词输入框显示输入的文本

    当 用户未填写规则描述
    或者 用户未填写提示词
    并且 用户尝试保存
    那么 显示验证错误提示
    并且 阻止保存操作

  场景: 验证必填字段
    假设 用户正在创建新的 Codegen 配置
    当 用户未填写标题 (title)
    并且 用户尝试保存
    那么 显示"标题为必填项"的错误提示
    并且 阻止保存操作

    当 用户未填写描述 (description)
    并且 用户尝试保存
    那么 显示"描述为必填项"的错误提示
    并且 阻止保存操作

    当 用户未选择技术栈 (fullStack)
    并且 用户尝试保存
    那么 显示"技术栈为必填项"的错误提示
    并且 阻止保存操作

    当 用户未填写 AI 模型 (model)
    并且 用户尝试保存
    那么 显示"AI 模型为必填项"的错误提示
    并且 阻止保存操作

    当 用户未填写代码渲染器 URL (codeRendererUrl)
    并且 用户尝试保存
    那么 显示"代码渲染器 URL 为必填项"的错误提示
    并且 阻止保存操作

  场景: 验证技术栈选项
    假设 用户正在创建或编辑 Codegen 配置
    当 用户选择技术栈 (fullStack)
    那么 显示技术栈下拉选择框
    并且 选项包含 "React" 和 "Vue"

    当 用户选择 "React"
    那么 技术栈字段值为 "React"

    当 用户选择 "Vue"
    那么 技术栈字段值为 "Vue"

  场景: 管理指南列表
    假设 用户正在创建或编辑 Codegen 配置
    当 用户点击"添加指南"按钮
    那么 显示新的指南输入框

    当 用户输入指南内容为 "生成登录页面"
    并且 用户点击"确认"按钮
    那么 指南列表中添加该指南

    当 用户点击指南列表中的"删除"按钮
    那么 该指南从列表中移除

    当 用户修改指南内容
    并且 用户点击"保存"按钮
    那么 指南列表更新为修改后的内容

  场景: 管理规则列表
    假设 用户正在创建或编辑 Codegen 配置
    并且 配置中已存在至少一条规则
    当 用户点击规则列表中的"删除"按钮
    那么 显示确认删除对话框

    当 用户确认删除
    那么 该规则从列表中移除

    当 用户取消删除
    那么 规则列表保持不变

    当 用户点击规则列表中的"编辑"按钮
    那么 显示规则编辑表单
    并且 表单中预填充了该规则的数据

    当 用户修改规则内容
    并且 用户点击"保存"按钮
    那么 规则列表更新为修改后的内容

  场景: 预览配置
    假设 用户正在创建或编辑 Codegen 配置
    并且 用户已填写部分或全部配置信息
    当 用户点击"预览"按钮
    那么 显示配置预览面板
    并且 预览面板以 JSON 格式显示当前配置数据

    当 用户修改配置信息
    那么 预览面板实时更新显示

  场景: 取消编辑
    假设 用户正在创建或编辑 Codegen 配置
    并且 用户已修改配置信息
    当 用户点击"取消"按钮
    那么 显示确认对话框

    当 用户确认取消
    那么 丢弃所有未保存的修改
    并且 返回配置列表页面

    当 用户取消操作
    那么 继续停留在编辑页面

  场景: 复制现有配置
    假设 用户进入 Codegen 配置管理页面
    并且 系统中已存在一个 Codegen 配置
    当 用户点击配置列表中的"复制"按钮
    那么 创建该配置的副本
    并且 显示编辑表单
    并且 标题字段自动添加 "(副本)" 后缀
    并且 其他字段保持原配置的值

    当 用户修改副本配置
    并且 用户点击"保存"按钮
    那么 保存为新配置
    并且 原配置保持不变

2、打开 Cursor Agent,选择代码能力最强的模型,并输入:

compoder generate:biz-component @components/biz/CodegenConfig/CodegenConfig.feature ,生成的代码保存到 @components/biz/CodegenConfig

3、运行 Storybook

pnpm storybook

AI 驱动页面对接联调实现

Cursor Project Rules
API 请求服务层

统一管理前端需要调用的 API 请求,包括请求参数类型、响应数据类型等。

.cursor/rules/generate-services.mdc

---
description: compoder generate:services
globs:
---
# Service Layer Code Generation Rules

## Input Files Pattern

- `@/app/api/{module}/types.d.ts`: Contains API interface definitions
- `@/app/api/{module}/{action}/route.ts`: Contains API route implementations

## Output Files Pattern

- `@/app/services/{module}/{module}.service.ts`: Service layer implementation

## Generation Rules

1. Service File Structure:

```typescript
import { getInstance } from '../request'
import { {ModuleName}Api } from '@/app/api/{module}/types'

const request = getInstance()

export const {actionName}{ModuleName} = async (
  params: {ModuleName}Api.{Action}Request
): Promise<{ModuleName}Api.{Action}Response> => {
  try {
    // For GET requests
    if ("{method}" === "GET") {
      const filteredParams = Object.fromEntries(
        Object.entries(params).filter(([, value]) => value !== undefined)
      )
      const queryString = new URLSearchParams(
        filteredParams as Record<string, string>
      ).toString()
      const response = await request(`/{module}/{action}?${queryString}`, {
        method: "GET",
      })
      return await response.json()
    }

    // For POST/PUT requests
    if ("{method}" === "POST" || "{method}" === "PUT") {
      const response = await request(`/{module}/{action}`, {
        method: "{method}",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(params),
      })
      return await response.json()
    }

    // For DELETE requests
    if ("{method}" === "DELETE") {
      const response = await request(`/{module}/{action}`, {
        method: "DELETE",
      })
      return await response.json()
    }

    throw new Error(`Unsupported HTTP method: {method}`)
  } catch (error) {
    // Error will be handled by request.ts
    throw error
  }
}
```

2. Function Naming Convention:

- For GET endpoints: use `get{ModuleName}{Action}` (e.g., `getUserList`, `getOrderDetail`)
- For POST endpoints: use `create{ModuleName}`
- For PUT endpoints: use `update{ModuleName}`
- For DELETE endpoints: use `delete{ModuleName}`

3. Type Definition Pattern:

```typescript
declare namespace {ModuleName}Api {
  export interface {Action}Request {
    // Request parameters
  }

  export interface {Action}Response {
    // Response data structure
  }
}
```

## Variables Explanation

- `{module}`: The lowercase module name (e.g., "user", "order", "codegen")
- `{ModuleName}`: The PascalCase module name (e.g., "User", "Order", "Codegen")
- `{action}`: The action name (e.g., "list", "detail", "create")
- `{Action}`: The PascalCase action name (e.g., "List", "Detail", "Create")
- `{method}`: HTTP method in lowercase (e.g., "get", "post", "put", "delete")
- `{requestConfig}`: Request configuration object based on HTTP method:
  - GET: `{ params }`
  - POST/PUT: `data`
  - DELETE: `{ params }` or empty

## Example

For a module named "codegen" with a "list" action:

Input types.d.ts:

```typescript
declare namespace CodegenApi {
  export interface ListRequest {
    page: number
    pageSize: number
    name?: string
    fullStack?: "React" | "Vue"
  }

  export interface ListResponse {
    data: any[]
    total: number
  }
}
```

Generated service.ts:

```typescript
import { getInstance } from "../request"
import { CodegenApi } from "@/app/api/codegen/types"

const request = getInstance()

export const getCodegenList = async (
  params: CodegenApi.ListRequest,
): Promise<CodegenApi.ListResponse> => {
  const filteredParams = Object.fromEntries(
    Object.entries(params).filter(([, value]) => value !== undefined),
  )
  const queryString = new URLSearchParams(
    filteredParams as Record<string, string>,
  ).toString()
  const response = await request(`/codegen/list?${queryString}`, {
    method: "GET",
  })
  return response.json()
}
```

## Notes

1. For GET requests, transform parameters into URL query string
2. Filter out undefined values from parameters
3. Use URLSearchParams for proper URL encoding
4. Use proper error handling and async/await patterns
5. Keep service functions focused and single-responsibility

翻译为中文:

---
description: compoder generate:services
globs:
---
# Service 层代码生成规则

## 输入文件约定

- `@/app/api/{module}/types.d.ts`:包含 API 接口定义
- `@/app/api/{module}/{action}/route.ts`:包含 API 路由实现

## 输出文件约定

- `@/app/services/{module}/{module}.service.ts`Service 层实现

## 生成规则

1. Service 文件结构:

```typescript
import { getInstance } from '../request'
import { {ModuleName}Api } from '@/app/api/{module}/types'

const request = getInstance()

export const {actionName}{ModuleName} = async (
  params: {ModuleName}Api.{Action}Request
): Promise<{ModuleName}Api.{Action}Response> => {
  try {
    // GET 请求
    if ("{method}" === "GET") {
      const filteredParams = Object.fromEntries(
        Object.entries(params).filter(([, value]) => value !== undefined)
      )
      const queryString = new URLSearchParams(
        filteredParams as Record<string, string>
      ).toString()
      const response = await request(`/{module}/{action}?${queryString}`, {
        method: "GET",
      })
      return await response.json()
    }

    // POST/PUT 请求
    if ("{method}" === "POST" || "{method}" === "PUT") {
      const response = await request(`/{module}/{action}`, {
        method: "{method}",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(params),
      })
      return await response.json()
    }

    // DELETE 请求
    if ("{method}" === "DELETE") {
      const response = await request(`/{module}/{action}`, {
        method: "DELETE",
      })
      return await response.json()
    }

    throw new Error(`Unsupported HTTP method: {method}`)
  } catch (error) {
    // 错误由 request.ts 统一处理
    throw error
  }
}
```

2. 函数命名约定:

- GET 接口:使用 `get{ModuleName}{Action}`(如 `getUserList``getOrderDetail`
- POST 接口:使用 `create{ModuleName}`
- PUT 接口:使用 `update{ModuleName}`
- DELETE 接口:使用 `delete{ModuleName}`

3. 类型定义约定:

```typescript
declare namespace {ModuleName}Api {
  export interface {Action}Request {
    // 请求参数
  }

  export interface {Action}Response {
    // 响应数据结构
  }
}
```

## 变量说明

- `{module}`:小写模块名(如 "user""order""codegen"
- `{ModuleName}`:帕斯卡命名模块名(如 "User""Order""Codegen"
- `{action}`:动作名(如 "list""detail""create"
- `{Action}`:帕斯卡命名动作名(如 "List""Detail""Create"
- `{method}`:小写 HTTP 方法(如 "get""post""put""delete"
- `{requestConfig}`:按 HTTP 方法区分的请求配置对象:
  - GET`{ params }`
  - POST/PUT`data`
  - DELETE`{ params }` 或空

## 示例

模块名为 "codegen"、动作为 "list" 时:

输入 types.d.ts

```typescript
declare namespace CodegenApi {
  export interface ListRequest {
    page: number
    pageSize: number
    name?: string
    fullStack?: "React" | "Vue"
  }

  export interface ListResponse {
    data: any[]
    total: number
  }
}
```

生成的 service.ts

```typescript
import { getInstance } from "../request"
import { CodegenApi } from "@/app/api/codegen/types"

const request = getInstance()

export const getCodegenList = async (
  params: CodegenApi.ListRequest,
): Promise<CodegenApi.ListResponse> => {
  const filteredParams = Object.fromEntries(
    Object.entries(params).filter(([, value]) => value !== undefined),
  )
  const queryString = new URLSearchParams(
    filteredParams as Record<string, string>,
  ).toString()
  const response = await request(`/codegen/list?${queryString}`, {
    method: "GET",
  })
  return response.json()
}
```

## 注意事项

1. GET 请求需将参数转换为 URL 查询字符串
2. 从参数中过滤掉值为 undefined 的字段
3. 使用 URLSearchParams 做正确的 URL 编码
4. 使用合理的错误处理与 async/await 写法
5. 保持 service 函数职责单一、聚焦
server-store 层 & 页面层
  • server-store 层 - 统一管理页面需要对接的后台 api 数据
  • 页面层 - 拼装业务组件并接入 server-store 数据

.cursor/rules/generate-page-integration.mdc

---
description: compoder generate:page-integration
globs:
---
# Page Integration Code Generation Guide

This guide helps generate the integration layer between business components and APIs, including server-store and page components.

## Input Requirements

1. Business Component Usage Examples (以下任一形式):

   - Component Story file (e.g., `ComponentName.stories.tsx`)
   - Mock implementation in existing pages (e.g., `page.tsx` with mock data)
   - Example usage with hardcoded data
   - Test files showing component usage

   Example:

   ```typescript
   // From stories
   export const Default = {
     args: {
       items: mockItems,
       onEditClick: (id) => console.log('Edit:', id)
     }
   }

   // Or from page with mock data
   <ComponentName
     items={[
       { id: '1', title: 'Item 1' },
       { id: '2', title: 'Item 2' }
     ]}
     onEditClick={(id) => console.log('Edit clicked:', id)}
   />
```

2. Component Interface Information (one of the following):
   - Interface/type definition files
   - Props types from component file
   - TypeScript types inferred from usage examples

3. API Related Files:
   - Service file (e.g., `*.service.ts`) containing API calls
   - API types (request/response types)

## Output Structure

Important Note: When generating code for existing files:

- NEVER remove or replace existing selectors/mutations
- ONLY add new selectors/mutations incrementally
- Preserve all existing functionality
- Follow the existing patterns and naming conventions

The generator should create or update the following files:

### 1. server-store/selectors.ts (if needed)

- Create query hooks using @tanstack/react-query
- Transform API response data to match component prop types in the select function
- Example structure:

Example of a new selector (while keeping existing ones):

```typescript
// Original example - keep this
export const useGetSomeData = (params: ApiType.Request) => {
  return useQuery<
    ApiType.Response,
    Error,
    ComponentDataType // Transformed type matching component props
  >({
    queryKey: ["queryName", params],
    queryFn: () => apiCall(params),
    select: (data) => ({
      // Transform API response to component data structure
      ...transformedData,
    }),
  });
};

// When adding new selectors, add them below existing ones
export const useNewSelector = () => {
  return useQuery({
    // ... new selector implementation
  });
};
```

### 2. server-store/mutations.ts (if needed)

Example of mutations (while keeping existing ones):

```typescript
// Original example - keep this
export const useSomeDataMutation = () => {
  return useMutation<ApiType.Response, Error, ApiType.Request>({
    mutationFn: (params) => apiMutationCall(params),
  });
};

// When adding new mutations, add them below existing ones
export const useNewMutation = () => {
  return useMutation({
    // ... new mutation implementation
  });
};
```

### 3. page.tsx

Integrate the business component with server-store:

```typescript
export default function Page() {
  // 1. Use selectors/mutations
  const { data, isLoading } = useGetSomeData(params)
  const mutation = useSomeDataMutation()

  // 2. Handle component callbacks
  const handleSomeEvent = () => {
    mutation.mutate(...)
  }

  // 3. Render component with props
  return (
    <BusinessComponent
      data={data}
      isLoading={isLoading}
      onSomeEvent={handleSomeEvent}
      {...otherProps}
    />
  )
}
```

## Key Points to Consider

1. Data Transformation:
   - Ensure proper type conversion between API and component data structures
   - Handle null/undefined cases
   - Transform IDs, dates, and other special fields appropriately

2. Error Handling:
   - Include error states and error handling in queries/mutations
   - Provide appropriate error feedback in the UI

3. Loading States:
   - Handle loading states properly
   - Consider skeleton loaders or loading indicators

4. Props Mapping:
   - Map all required component props
   - Provide reasonable defaults when needed
   - Handle optional props appropriately

5. Event Handlers:
   - Implement all necessary callback functions
   - Handle async operations properly
   - Consider optimistic updates when appropriate

## Example Usage

Given:

- A CodegenFilterContainer component
- Codegen service API
- Related interfaces

Generate:

1. A selector for fetching codegen data
2. Mutations for any data modifications
3. A page component that:
   - Fetches data using the selector
   - Handles pagination/filtering
   - Renders the CodegenFilterContainer with appropriate props
   - Implements all necessary callbacks

The generated code should follow the project's existing patterns and naming conventions.

## Additional Guidelines for Incremental Updates

1. Code Preservation:
   - Always check existing files before adding new code
   - Never remove or modify existing selectors/mutations
   - Add new functionality below existing code
   - Maintain consistent formatting with existing code

2. Integration Strategy:
   - Review existing patterns in the codebase
   - Follow established naming conventions
   - Reuse existing utility functions when possible
   - Add new functionality incrementally

翻译为中文:


---

description: compoder generate:page-integration
globs:

---

# 页面集成代码生成指南

本指南用于生成业务组件与 API 之间的集成层,包括 server-store 与页面组件。

## 输入要求

1. 业务组件使用示例(以下任一形式):
   - 组件 Story 文件(如 `ComponentName.stories.tsx`)
   - 现有页面中的 Mock 实现(如带 mock 数据的 `page.tsx`)
   - 带硬编码数据的示例用法
   - 展示组件用法的测试文件

   示例:

   ```typescript
   // 来自 stories
   export const Default = {
     args: {
       items: mockItems,
       onEditClick: (id) => console.log('Edit:', id)
     }
   }

   // 或来自带 mock 数据的页面
   <ComponentName
     items={[
       { id: '1', title: 'Item 1' },
       { id: '2', title: 'Item 2' }
     ]}
     onEditClick={(id) => console.log('Edit clicked:', id)}
   />
   ```

2. 组件接口信息(以下任一):
   - 接口/类型定义文件
   - 组件文件中的 Props 类型
   - 从使用示例推断出的 TypeScript 类型

3. API 相关文件:
   - 包含 API 调用的 Service 文件(如 `*.service.ts`)
   - API 类型(请求/响应类型)

## 输出结构

重要说明:在为已有文件生成代码时:

- 切勿删除或替换已有的 selectors/mutations
- 仅以增量方式添加新的 selectors/mutations
- 保留所有已有功能
- 遵循现有模式与命名约定

生成器应创建或更新以下文件:

### 1. server-store/selectors.ts(如需要)

- 使用 @tanstack/react-query 创建 query hooks
-  select 函数中将 API 响应数据转换为与组件 props 类型一致
- 示例结构:

新增 selector 示例(同时保留已有内容):

```typescript
// 原有示例 - 保留
export const useGetSomeData = (params: ApiType.Request) => {
  return useQuery<
    ApiType.Response,
    Error,
    ComponentDataType // 转换后类型与组件 props 一致
  >({
    queryKey: ["queryName", params],
    queryFn: () => apiCall(params),
    select: (data) => ({
      //  API 响应转换为组件数据结构
      ...transformedData,
    }),
  });
};

// 添加新 selector 时,在已有内容下方追加
export const useNewSelector = () => {
  return useQuery({
    // ...  selector 实现
  });
};
```

### 2. server-store/mutations.ts(如需要)

mutations 示例(同时保留已有内容):

```typescript
// 原有示例 - 保留
export const useSomeDataMutation = () => {
  return useMutation<ApiType.Response, Error, ApiType.Request>({
    mutationFn: (params) => apiMutationCall(params),
  });
};

// 添加新 mutation 时,在已有内容下方追加
export const useNewMutation = () => {
  return useMutation({
    // ...  mutation 实现
  });
};
```

### 3. page.tsx

将业务组件与 server-store 集成:

```typescript
export default function Page() {
  // 1. 使用 selectors/mutations
  const { data, isLoading } = useGetSomeData(params)
  const mutation = useSomeDataMutation()

  // 2. 处理组件回调
  const handleSomeEvent = () => {
    mutation.mutate(...)
  }

  // 3. 使用 props 渲染组件
  return (
    <BusinessComponent
      data={data}
      isLoading={isLoading}
      onSomeEvent={handleSomeEvent}
      {...otherProps}
    />
  )
}
```

## 注意事项

1. 数据转换:
   - 确保 API 与组件数据结构之间的类型转换正确
   - 处理 null/undefined 情况
   -  ID、日期等特殊字段做适当转换

2. 错误处理:
   -  queries/mutations 中包含错误状态与错误处理
   -  UI 中提供合适的错误反馈

3. 加载状态:
   - 正确处理加载状态
   - 可考虑骨架屏或加载指示器

4. Props 映射:
   - 映射所有必需的组件 props
   - 在需要时提供合理默认值
   - 妥善处理可选 props

5. 事件处理:
   - 实现所有必要的回调函数
   - 正确处理异步操作
   - 在合适场景下考虑乐观更新

## 使用示例

给定:

- CodegenFilterContainer 组件
- Codegen 服务 API
- 相关接口定义

生成:

1. 用于获取 codegen 数据的 selector
2. 用于数据修改的 mutations
3. 页面组件,需:
   - 使用 selector 拉取数据
   - 处理分页/筛选
   - 用合适的 props 渲染 CodegenFilterContainer
   - 实现所有必要回调

生成代码应遵循项目现有的模式与命名约定。

## 增量更新补充说明

1. 代码保留:
   - 添加新代码前务必先检查已有文件
   - 不得删除或修改已有 selectors/mutations
   - 在已有代码下方追加新功能
   - 与现有代码保持一致的格式

2. 集成策略:
   - 参考代码库中的现有模式
   - 遵循既定命名约定
   - 尽可能复用已有工具函数
   - 以增量方式添加新功能
案例

下面我们来实现 Codegen 页面。

1、配置页面路由

新建 app/main/codegen/page.tsx。

"use client";

export default function Codegen() {
  return <div>codegen list</div>;
}

2、引入业务组件构建页面布局

"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { AppHeader } from "@/components/biz/AppHeader";
import { CodegenFilterContainer } from "@/components/biz/CodegenFilterContainer";
import { CodegenList } from "@/components/biz/CodegenList";

export default function Codegen() {
  const router = useRouter();
  const [filters, setFilters] = useState<{
    pageSize: number;
    selectedStack?: "All" | "React" | "Vue";
    searchKeyword?: string;
  }>({
    pageSize: 10,
    selectedStack: undefined,
    searchKeyword: undefined,
  });
  const handleStackChange = (stack: "All" | "React" | "Vue") => {
    setFilters({ ...filters, selectedStack: stack });
  };
  const handleSearchChange = (keyword: string) => {
    setFilters({ ...filters, searchKeyword: keyword });
  };
  const handleLoadMore = () => {};

  const handleItemClick = (id: string) => {
    router.push(`/main/codegen/${id}`);
  };
  return (
    <div>
      <AppHeader breadcrumbs={[{ label: "Codegen" }]} />
      <CodegenFilterContainer
        selectedStack={filters.selectedStack}
        onStackChange={handleStackChange}
        onSearchChange={handleSearchChange}
        onLoadMore={handleLoadMore}
        isLoading={false}
        hasMore
      >
        <CodegenList
          onItemClick={handleItemClick}
          items={[
            {
              description:
                "A customizable button component with various styles",
              fullStack: "React",
              id: "1",
              title: "Button Component",
            },
            {
              description: "A flexible input field with validation",
              fullStack: "Vue",
              id: "2",
              title: "Input Field",
            },
            {
              description: "A modal dialog with customizable content",
              fullStack: "React",
              id: "3",
              title: "Modal Dialog",
            },
            {
              description: "A dropdown menu with customizable content",
              fullStack: "Vue",
              id: "4",
              title: "Dropdown Menu",
            },
            {
              description: "A tooltip with customizable content",
              fullStack: "React",
              id: "5",
              title: "Tooltip",
            },
            {
              description: "An accordion component with customizable content",
              fullStack: "Vue",
              id: "6",
              title: "Accordion",
            },
          ]}
        />
      </CodegenFilterContainer>
    </div>
  );
}

3、生成 Service 请求

打开 Cursor Agent,选择代码能力最强的模型,输入:

compoder generate:services @app/api/codegen/types.d.ts:5-15 @app/api/codegen/list/route.ts

输出:app/services/codegen/codegen.service.ts

import { getInstance } from "../request";
import type { CodegenApi } from "@/app/api/codegen/types";

const request = getInstance();

export const getCodegenList = async (
  params: CodegenApi.ListRequest,
): Promise<CodegenApi.ListResponse> => {
  try {
    const filteredParams = Object.fromEntries(
      Object.entries(params).filter(([, value]) => value !== undefined),
    );
    const queryString = new URLSearchParams(
      filteredParams as Record<string, string>,
    ).toString();
    const response = await request(`/codegen/list?${queryString}`, {
      method: "GET",
    });
    return await response.json();
  } catch (error) {
    throw error;
  }
};

4、给页面对接 API

打开 Cursor Agent,选择代码能力最强的模型,输入:

compoder generate:page-integration @app/main/codegen/page.tsx @app/services/codegen/codegen.service.ts @app/api/codegen/types.d.ts:5-15

输出:

(1)、app/main/codegen/server-store/selectors.ts

import { useInfiniteQuery } from "@tanstack/react-query";
import { getCodegenList } from "@/app/services/codegen/codegen.service";
import type { CodegenApi } from "@/app/api/codegen/types";
import type { JobItem } from "@/components/biz/CodegenList/interface";

type ListParams = Omit<CodegenApi.ListRequest, "page">;

function transformToListItems(
  data: CodegenApi.ListResponse["data"],
): JobItem[] {
  return data.map((item) => ({
    id: String((item as { _id: unknown })._id),
    title: item.title,
    description: item.description,
    fullStack: item.fullStack,
  }));
}

export const useGetCodegenList = (params: ListParams) => {
  return useInfiniteQuery<
    CodegenApi.ListResponse,
    Error,
    { items: JobItem[]; total: number }
  >({
    queryKey: ["codegenList", params],
    queryFn: ({ pageParam }) =>
      getCodegenList({ ...params, page: pageParam as number }),
    initialPageParam: 1,
    getNextPageParam: (lastPage, allPages) => {
      const loadedCount = allPages.reduce(
        (sum, page) => sum + page.data.length,
        0,
      );
      return loadedCount < lastPage.total ? allPages.length + 1 : undefined;
    },
    select: (data) => ({
      items: data.pages.flatMap((page) => transformToListItems(page.data)),
      total: data.pages[data.pages.length - 1]?.total ?? 0,
    }),
  });
};

(2)、app/main/codegen/page.tsx

"use client";

import { useState, useMemo } from "react";
import { useRouter } from "next/navigation";
import { AppHeader } from "@/components/biz/AppHeader";
import { CodegenFilterContainer } from "@/components/biz/CodegenFilterContainer";
import { CodegenList } from "@/components/biz/CodegenList";
import { useGetCodegenList } from "./server-store/selectors";

export default function Codegen() {
  const router = useRouter();
  const [filters, setFilters] = useState<{
    pageSize: number;
    selectedStack?: "All" | "React" | "Vue";
    searchKeyword?: string;
  }>({
    pageSize: 10,
    selectedStack: undefined,
    searchKeyword: undefined,
  });

  const listParams = useMemo(
    () => ({
      pageSize: filters.pageSize,
      name: filters.searchKeyword || undefined,
      fullStack:
        filters.selectedStack && filters.selectedStack !== "All"
          ? filters.selectedStack
          : undefined,
    }),
    [filters.pageSize, filters.searchKeyword, filters.selectedStack],
  );

  const {
    data,
    isLoading: isInitialLoading,
    isFetchingNextPage,
    hasNextPage,
    fetchNextPage,
  } = useGetCodegenList(listParams);

  const handleStackChange = (stack: "All" | "React" | "Vue") => {
    setFilters((prev) => ({ ...prev, selectedStack: stack }));
  };

  const handleSearchChange = (keyword: string) => {
    setFilters((prev) => ({ ...prev, searchKeyword: keyword }));
  };

  const handleLoadMore = () => {
    if (hasNextPage && !isFetchingNextPage) {
      fetchNextPage();
    }
  };

  const handleItemClick = (id: string) => {
    router.push(`/main/codegen/${id}`);
  };

  return (
    <div>
      <AppHeader breadcrumbs={[{ label: "Codegen" }]} />
      <CodegenFilterContainer
        selectedStack={filters.selectedStack}
        onStackChange={handleStackChange}
        onSearchChange={handleSearchChange}
        onLoadMore={handleLoadMore}
        isLoading={isFetchingNextPage}
        hasMore={!!hasNextPage}
      >
        {isInitialLoading ? (
          <div className="py-8 text-center text-muted-foreground">
            Loading...
          </div>
        ) : (
          <CodegenList
            onItemClick={handleItemClick}
            items={data?.items ?? []}
          />
        )}
      </CodegenFilterContainer>
    </div>
  );
}

AI 工作流模块实现

工作流程说明:

1、设计组件(designComponent)

分析用户需求,确定组件名称与描述,确定所需要库与组件,为后续生成做准备。

2、生成组件(generateComponent)

基于设计阶段的结果,调用 AI 大模型流式生成组件代码,实时返回生成结果。

3、存储组件(storeComponent)

处理生成的组件代码,判断是新建还是更新组件,根据情况合并代码,并将结果保存到数据库中。

工作流入口:

export const componentWorkflow = pipe<InitialWorkflowContext, WorkflowContext>(
  withErrorHandling(designComponent),
  withErrorHandling(generateComponent),
  withErrorHandling(storeComponent),
);

export async function run(context: InitialWorkflowContext) {
  try {
    const result = await componentWorkflow(context);
    return {
      success: true,
      data: result.state,
    };
  } catch (error: any) {
    console.error("Workflow failed:", error);
    context.stream.write(error.toString());
    context.stream.close();
  }
}

设计组件(designComponent)

export async function generateComponentDesign(
  req: WorkflowContext,
): Promise<ComponentDesign> {
  const componentsSchema = z.object({
    componentName: z.string().describe("Component name"),
    componentDescription: z.string().describe("Component description"),
    library: z.array(
      z.object({
        name: z.string().describe("Library name"),
        components: z
          .array(z.string())
          .describe("Components name in the library"),
        description: z
          .string()
          .describe(
            "Describe how each component in components is used in a table format",
          ),
      }),
    ),
  });

  let parserCompletion: ComponentDesign = {
    componentName: "componentName",
    componentDescription: "componentDescription",
    library: [],
  };

  // 构建系统提示词
  const systemPrompt = buildSystemPrompt(req.query.rules);

  console.log("design-component systemPrompt:", systemPrompt);
  const messages = [
    ...buildCurrentComponentMessage(req.query.component),
    ...buildUserMessage(req.query.prompt),
  ];

  try {
    const stream = await streamText({
      system: systemPrompt,
      model: req.query.aiModel,
      messages,
    });

    let accumulatedJson = "";

    for await (const part of stream.textStream) {
      req.stream.write(part);
      accumulatedJson += part;
    }

    try {
      if (!accumulatedJson) {
        throw new Error(
          "No response from the AI, please check the providers configuration and the apiKey balance",
        );
      }
      // Try to extract JSON from the response
      const jsonMatch = accumulatedJson.match(/\{[\s\S]*\}/);
      if (!jsonMatch) {
        throw new Error("No valid JSON found in the response");
      }

      console.log("jsonMatch", jsonMatch[0]);

      // Fix backticks in the JSON string by replacing them with escaped double quotes
      let jsonString = jsonMatch[0];

      // 1. Handle existing escape characters first
      jsonString = jsonString.replace(/\\`/g, "\\\\`");

      // 2. Process content wrapped in backticks
      jsonString = jsonString.replace(
        /`((?:[^`\\]|\\.|\\`)*)`/g, // More precise regular expression
        function (match, content) {
          // Handle special characters
          const escaped = content
            .replace(/\\/g, "\\\\") // Handle backslashes first
            .replace(/"/g, '\\"') // Handle double quotes
            .replace(/\n/g, "\\n") // Handle newlines
            .replace(/\r/g, "\\r") // Handle carriage returns
            .replace(/\t/g, "\\t"); // Handle tabs
          return `"${escaped}"`;
        },
      );

      const parsedJson = JSON.parse(jsonString);

      console.log("parsedJson", parsedJson);

      // Validate the parsed JSON against our schema
      const validatedResult = componentsSchema.parse(parsedJson);
      parserCompletion = validatedResult;
    } catch (parseError) {
      throw new Error(
        `Failed to parse AI response as valid JSON: ${parseError}`,
      );
    }

    if (parserCompletion.library.length > 0) {
      const docs = getPrivateComponentDocs(req.query.rules);
      parserCompletion.retrievedAugmentationContent =
        getRetrievedAugmentationContent(docs, parserCompletion.library);
    }

    return parserCompletion;
  } catch (err: unknown) {
    console.log("err", err);
    if (err instanceof Error) {
      throw err;
    }
    throw new Error(String(err));
  }
}
  • 构建 system prompt
/**
 * 根据工作流规则构建设计组件的系统提示词。
 * 若规则中包含组件库文档,则要求模型从「基础组件素材」中抽取;否则仅抽取组件名与描述。
 *
 * @param rules - 工作流上下文中的规则(含公共/私有组件库配置)
 * @returns 拼接好的系统提示词字符串
 */
const buildSystemPrompt = (rules: WorkflowContext["query"]["rules"]) => {
  // 从规则中获取公共/私有组件库的文档描述(用于约束可选的基础组件素材)
  const componentsDescription = getPrivateDocsDescription(rules);
  const hasComponentLibraries = !!componentsDescription;

  // 按「有组件库 / 无组件库」两种场景分别定义提示词片段
  const promptParts = {
    /** 有组件库:需从基础素材中抽取组件名、描述及所用库与组件列表 */
    withLibraries: {
      goal: 'Extract the "basic component materials", component name, and description information needed to develop business components from business requirements and design drafts.',
      constraints: `Basic component materials include:
    ${componentsDescription}
    Please note: You should not provide example code and any other text in your response, only provider json response.`,
      responseFormat: `{
      "componentName": string, // Component name
      "componentDescription": string, // Component description
      "library": [ // Libraries containing required base material components
        {
          "name": string, // Library name
          "components": string[], // Components name in the library
          "description": string // Describe how each component in components is used in a table format
        }
      ]
    }`,
      workflowStep2:
        "2. Extract required materials from [Constraints] basic component materials for developing business components",
    },
    /** 无组件库:仅抽取组件名与描述,不涉及库与基础素材 */
    withoutLibraries: {
      goal: "Extract component name and description information needed to develop business components from business requirements and design drafts.",
      constraints: `- Extract the component name and description information from the business requirements and design drafts. 
- Analyze the design draft to understand the business functionality needed.

Please note: You should not provide example code and any other text in your response, only provider json response.`,
      responseFormat: `{
      "componentName": string, // Component name
      "componentDescription": string // Component description that clearly explains the purpose and functionality
    }`,
      workflowStep2:
        "2. Analyze the business requirements and design drafts to identify needed business components and their functions",
    },
  };

  // 根据是否存在组件库描述,选择对应的提示词片段
  const parts = hasComponentLibraries
    ? promptParts.withLibraries
    : promptParts.withoutLibraries;

  // 拼接工作流步骤(三步:接收需求 → 抽取/分析 → 输出 JSON)
  const workflowSteps = `1. Accept user's business requirements or design draft images
    ${parts.workflowStep2}
    3. Generate and return the JSON response in the specified format`;

  // 组装最终的系统提示词(角色 + 目标 + 约束 + 响应格式 + 工作流)
  return `
    # You are a senior frontend engineer who excels at developing business components.
    
    ## Goal
    ${parts.goal}
    
    ## Constraints
    ${parts.constraints}
    
    ## Response Format
    You must respond with a JSON object in the following format:
    ${parts.responseFormat}
    
    ## Workflow
    ${workflowSteps}
  `;
};
  • 构建 messages
/**
 * 当已存在当前组件时,构造对应的「用户消息 + 助手消息」,用于上下文续写。
 * 用户消息:组件的 prompt(文本/图片);助手消息:组件名称与代码,作为已有结果供模型参考。
 *
 * @param component - 工作流中的当前组件(含 prompt、name、code)
 * @returns 若 component 存在则返回 [userMessage, assistantMessage],否则返回空数组
 */
const buildCurrentComponentMessage = (
  component: WorkflowContext["query"]["component"],
): Array<CoreMessage> => {
  return component
    ? [
        {
          role: "user",
          content:
            component?.prompt?.map((prompt) => {
              if (prompt.type === "image") {
                return { type: "image" as const, image: prompt.image };
              }
              return { type: "text" as const, text: prompt.text };
            }) || [],
        },
        {
          role: "assistant",
          content: `
        - Component name: ${component?.name}
        - Component code:
        ${component?.code}
      `,
        },
      ]
    : [];
};

/**
 * 将工作流中的用户 prompt(文本/图片混合)转成 AI 对话格式的一条用户消息。
 *
 * @param prompt - 用户输入的 prompt 数组,每项为 { type: "text" | "image", text?, image? }
 * @returns 仅包含一条 role 为 "user" 的 CoreMessage 数组,content 为多模态内容
 */
const buildUserMessage = (
  prompt: WorkflowContext["query"]["prompt"],
): Array<CoreMessage> => {
  return [
    {
      role: "user",
      content: prompt.map((p) => {
        if (p.type === "image") {
          return { type: "image" as const, image: p.image };
        }
        return { type: "text" as const, text: p.text };
      }),
    },
  ];
};
  • Vercel AI SDK 流式生成组件设计
try {
  // Vercel AI SDK 流式生成组件设计
  const { textStream } = await streamText({
    system: systemPrompt,
    model: req.query.aiModel,
    messages,
  });

  let accumulatedJson = "";

  // 流式写出到客户端并累积全文,供后续解析 JSON
  for await (const part of textStream) {
    req.stream.write(part);
    accumulatedJson += part;
  }

  // 从回复中抽取 JSON、处理反引号、解析并做 schema 校验
  try {
    if (!accumulatedJson) {
      throw new Error(
        "No response from the AI, please check the providers configuration and the apiKey balance",
      );
    }
    const jsonMatch = accumulatedJson.match(/\{[\s\S]*\}/);
    if (!jsonMatch) {
      throw new Error("No valid JSON found in the response");
    }

    console.log("jsonMatch", jsonMatch[0]);

    // 将模型可能返回的 markdown 代码块(反引号包裹)转为合法 JSON 字符串
    let jsonString = jsonMatch[0];

    jsonString = jsonString.replace(/\\`/g, "\\\\`");

    jsonString = jsonString.replace(
      /`((?:[^`\\]|\\.|\\`)*)`/g,
      function (match, content) {
        const escaped = content
          .replace(/\\/g, "\\\\")
          .replace(/"/g, '\\"')
          .replace(/\n/g, "\\n")
          .replace(/\r/g, "\\r")
          .replace(/\t/g, "\\t");
        return `"${escaped}"`;
      },
    );

    const parsedJson = JSON.parse(jsonString);

    console.log("parsedJson", parsedJson);

    const validatedResult = componentsSchema.parse(parsedJson);
    parserCompletion = validatedResult;
  } catch (parseError) {
    throw new Error(`Failed to parse AI response as valid JSON: ${parseError}`);
  }

  // 若设计了使用的库,则从私有文档中检索对应内容并挂到结果上
  if (parserCompletion.library.length > 0) {
    const docs = getPrivateComponentDocs(req.query.rules);
    parserCompletion.retrievedAugmentationContent =
      getRetrievedAugmentationContent(docs, parserCompletion.library);
  }

  return parserCompletion;
} catch (err: unknown) {
  console.log("err", err);
  if (err instanceof Error) {
    throw err;
  }
  throw new Error(String(err));
}

生成组件(generateComponent)

export const generateComponent = async (
  context: DesignProcessingWorkflowContext,
): Promise<GenerateProcessingWorkflowContext> => {
  context.stream.write("start call codegen-ai \n");

  let completion = "";

  const systemPrompt = buildSystemPrompt(
    context.query.rules,
    context.state?.designTask?.retrievedAugmentationContent,
  );

  console.log("generate-component systemPrompt:", systemPrompt);

  const messages = generateComponentMessage(context);

  const stream = await streamText({
    system: systemPrompt,
    model: context.query.aiModel,
    messages,
  });

  for await (const part of stream.textStream) {
    try {
      process.stdout.write(part || "");
      const chunk = part || "";
      completion += chunk;
      context.stream.write(chunk);
    } catch (e) {
      console.error(e);
    }
  }

  context.stream.write("call codegen-ai end \n\n");

  return {
    ...context,
    state: {
      ...context.state,
      generatedCode: completion,
    },
  };
};
  • 构建 system prompt
// Generate the output specification section
const generateOutputSpecification = (
  rules: WorkflowContext["query"]["rules"],
): string => {
  const fileStructure = getFileStructureRule(rules);
  if (!fileStructure) return "";

  return `
    ## Output Specification
    ${fileStructure}
  `;
};

// Generate the style specification section
const generateStyleSpecification = (
  rules: WorkflowContext["query"]["rules"],
): string => {
  const styles = getStylesRule(rules);
  if (!styles) return "";

  return `
    ## Style Specification
    ${styles}
  `;
};

// Generate the open source components section
const generateOpenSourceComponents = (
  rules: WorkflowContext["query"]["rules"],
): string => {
  const publicComponents = getPublicComponentsRule(rules);
  if (!publicComponents || publicComponents.length === 0) return "";

  return `
    **Open Source Components**
    - You can use components from ${publicComponents.join(", ")}
    - Use the latest stable version of APIs
  `;
};

// Generate the private components section
const generatePrivateComponents = (
  retrievedAugmentationContent?: string,
): string => {
  if (!retrievedAugmentationContent) return "";

  return `
    **Private Components**
    - Must strictly follow the API defined in the documentation below
    - Using undocumented private component APIs is prohibited
    <basic-component-docs>
      ${retrievedAugmentationContent}
    </basic-component-docs>
  `;
};

// Generate the additional rules section
const generateAdditionalRules = (
  rules: WorkflowContext["query"]["rules"],
): string => {
  const specialAttentionRules = getSpecialAttentionRules(rules);
  if (!specialAttentionRules) return "";

  return `
    ## Additional Rules
    ${specialAttentionRules}
  `;
};

/**
 * 构建「生成业务组件」步骤的系统提示词。
 * 按规则拼装:文件结构规范、样式规范、组件使用指南(开源 + 私有)、附加规则;
 * 仅当存在开源或私有组件说明时才包含「Component Usage Guidelines」小节。
 *
 * @param rules - 工作流中的规则(文件结构、样式、公开组件、特殊注意等)
 * @param retrievedAugmentationContent - 可选,私有组件文档检索结果,用于私有组件约束
 * @returns 拼接后的系统提示词字符串
 */
export const buildSystemPrompt = (
  rules: WorkflowContext["query"]["rules"],
  retrievedAugmentationContent?: string,
): string => {
  // 文件结构 prompt
  const outputSpecification = generateOutputSpecification(rules);
  // 样式 prompt
  const styleSpecification = generateStyleSpecification(rules);
  // 开源组件使用指南 prompt
  const openSourceComponents = generateOpenSourceComponents(rules);
  // 私有组件使用指南 prompt
  const privateComponents = generatePrivateComponents(
    retrievedAugmentationContent,
  );
  // 附加规则 prompt
  const additionalRules = generateAdditionalRules(rules);

  // 仅当存在开源或私有组件说明时,才添加「组件使用指南」标题与内容
  const hasComponentGuidelines = openSourceComponents || privateComponents;
  const componentGuidelinesHeader = hasComponentGuidelines
    ? "## Component Usage Guidelines\n"
    : "";

  const componentGuidelines = hasComponentGuidelines
    ? `${componentGuidelinesHeader}${openSourceComponents}${privateComponents}`
    : "";

  return `
    # You are a senior frontend engineer focused on business component development

    ## Goal
    Generate business component code based on user requirements
    ${outputSpecification}
    ${styleSpecification}
    ${componentGuidelines}
    ${additionalRules}
  `;
};
  • 构建 messages
/**
 * 当已存在当前组件时,构造对应的「用户消息 + 助手消息」,用于上下文续写。
 * 用户消息:组件的 prompt(文本/图片);助手消息:组件名称与代码,作为已有结果供模型参考。
 *
 * @param component - 工作流中的当前组件(含 prompt、name、code)
 * @returns 若 component 存在则返回 [userMessage, assistantMessage],否则返回空数组
 */
const buildCurrentComponentMessage = (
  component: WorkflowContext["query"]["component"],
): Array<CoreMessage> => {
  return component
    ? [
        {
          role: "user",
          content:
            component?.prompt?.map((prompt) => {
              if (prompt.type === "image") {
                return { type: "image" as const, image: prompt.image };
              }
              return { type: "text" as const, text: prompt.text };
            }) || [],
        },
        {
          role: "assistant",
          content: `
        - Component name: ${component?.name}
        - Component code:
        ${component?.code}
      `,
        },
      ]
    : [];
};

/**
 * 构建用于组件生成的用户消息
 *
 * 将用户原始 prompt 与设计阶段产出的组件设计信息合并,
 * 组装成符合 CoreMessage 格式的一条 user 消息,供大模型生成组件代码时使用。
 *
 * @param prompt - 用户输入的 prompt 列表,可包含文本和图片
 * @param design - 设计阶段产出的 designTask,含组件名、描述、使用的基础组件库等
 * @returns 单条 role 为 "user" 的 CoreMessage 数组
 */
export const buildUserMessage = (
  prompt: WorkflowContext["query"]["prompt"],
  design: NonNullable<WorkflowContext["state"]>["designTask"],
): Array<CoreMessage> => {
  return [
    {
      role: "user",
      content: prompt.map((p) => {
        // 图片类内容原样透传,不注入设计信息
        if (p.type === "image") {
          return { type: "image" as const, image: p.image };
        }
        // 文本类内容:用户需求 + 设计信息(组件名、描述、基础组件及使用说明)
        return {
          type: "text" as const,
          text: `<user-requirements>
        ${p.text}

        ## Component Design Information
        - Component Name: ${design?.componentName}
        - Component Description: ${design?.componentDescription}
        - Base Components Used:
        ${design?.library
          ?.map(
            (lib) => `
          ${lib.name}:
          - Component List: ${lib.components.join(", ")}
          - Usage Instructions: ${lib.description}
        `,
          )
          .join("\n")}
        </user-requirements>`,
        };
      }),
    },
  ];
};

// generate component prompt
export const generateComponentMessage = (
  context: WorkflowContext,
): Array<CoreMessage> => {
  if (!context.state?.designTask) {
    throw new Error("Design task is required but not found in context");
  }

  return [
    ...buildCurrentComponentMessage(context.query.component),
    ...buildUserMessage(context.query.prompt, context.state.designTask),
  ];
};
  • Vercel AI SDK 流式生成组件代码
// Vercel AI SDK 流式生成组件代码
const stream = await streamText({
  system: systemPrompt,
  model: context.query.aiModel,
  messages,
});

// 消费流式输出:同时输出到控制台、写入前端流,并累积完整内容
for await (const part of stream.textStream) {
  try {
    process.stdout.write(part || "");
    const chunk = part || "";
    completion += chunk;
    context.stream.write(chunk);
  } catch (e) {
    console.error(e);
  }
}

// 写入结束标记,便于前端识别生成完成
context.stream.write("call codegen-ai end \n\n");

// 将累积的生成代码写入 state,供后续步骤使用
return {
  ...context,
  state: {
    ...context.state,
    generatedCode: completion,
  },
};

存储组件(storeComponent)

import {
  createComponentCode,
  updateComponentCode,
} from "@/lib/db/componentCode/mutations";
import { GenerateProcessingWorkflowContext } from "../../type";
import { transformComponentArtifactFromXml } from "@/lib/xml-message-parser/parser";

// Helper function to merge component files
function mergeComponentFiles(originalXml: string, newXml: string): string {
  // 解析原始XML和新的XML
  const originalComponent = transformComponentArtifactFromXml(originalXml);
  const newComponent = transformComponentArtifactFromXml(newXml);

  if (!originalComponent || !newComponent) {
    // 如果无法解析,直接返回原始的XML
    return originalXml;
  }

  // 创建一个文件名到文件内容的映射
  const fileMap = new Map();

  // 先添加所有原始文件
  originalComponent.files.forEach((file) => {
    fileMap.set(file.name, {
      content: file.content,
      isEntryFile: file.isEntryFile,
    });
  });

  // 然后用新文件覆盖或添加
  newComponent.files.forEach((file) => {
    fileMap.set(file.name, {
      content: file.content,
      isEntryFile: file.isEntryFile,
    });
  });

  // 构建合并后的XML
  let mergedXml = `<ComponentArtifact name="${
    newComponent.componentName || originalComponent.componentName
  }">`;

  // 添加所有文件
  fileMap.forEach((file, fileName) => {
    mergedXml += `\n  <ComponentFile fileName="${fileName}" isEntryFile="${file.isEntryFile}">`;
    mergedXml += file.content;
    mergedXml += `</ComponentFile>`;
  });

  mergedXml += "\n</ComponentArtifact>";

  return mergedXml;
}

export const storeComponent = async (
  context: GenerateProcessingWorkflowContext,
): Promise<GenerateProcessingWorkflowContext> => {
  if (context.query.component) {
    // 获取原始代码和新生成的代码
    const originalCode = context.query.component.code;
    const newCode = context.state.generatedCode;

    // 合并组件文件
    const mergedCode = mergeComponentFiles(originalCode, newCode);

    // 更新组件代码
    await updateComponentCode({
      id: context.query.component.id,
      prompt: context.query.prompt,
      code: mergedCode,
    });
  } else {
    // 创建新组件代码
    const newComponent = await createComponentCode({
      userId: context.query.userId,
      codegenId: context.query.codegenId!,
      name: context.state.designTask.componentName,
      description: context.state.designTask.componentDescription,
      prompt: context.query.prompt,
      code: context.state.generatedCode,
    });
    context.stream.write(
      `<NewComponentId>${newComponent._id}</NewComponentId>`,
    );
  }

  context.stream.close();

  return context;
};

案例 - Shadcn UI

让 Compoder 帮我实现使用 Shadcn UI 组件库生成一个登录页的业务组件。

Codegen Rules
{
        "title": "Shadcn/UI Codegen",
        "description": "Code generator based on Shadcn/UI",
        "fullStack": "React",
        "guides": [
            "Generate a login page",
            "Generate a Table component, include 3 columns: name, age, address"
        ],
        "model": "gpt-4o",
        "codeRendererUrl": "http://localhost:3001",
        "rules": [
            {
                "type": "public-components",
                "description": "Define which public components to use",
                "dataSet": [
                    "shadcn/ui"
                ]
            },
            {
                "type": "styles",
                "description": "Define the rules for generating styles",
                "prompt": "Styles must be written using tailwindcss with full dark/light mode compatibility. Use Tailwind's dark mode utilities (dark:class-name) for theme variants. Prefer using color-scheme-neutral classes or theme-specific variants (dark:bg-gray-800/light:bg-white) to ensure consistent appearance in both dark and light modes."
            },
            {
                "type": "attention-rules",
                "description": "Attention rules for the code generator",
                "prompt": "Only use the following npm packages in the generated code: react, react-dom, lucide-react, next/link, next/image, @/lib/utils, framer-motion, react-hook-form, recharts, zod, and components from @/components/ui/* (shadcn base components). Do not import or use any other packages. Specifically, DO NOT use @hookform/resolvers/zod package - instead, manually set up form validation with zod without using this resolver."
            }
        ]
    },
AI 创建组件
设计组件(designComponent)
  • 构建 system prompt
# You are a senior frontend engineer who excels at developing business components.

    ## Goal
    Extract the "basic component materials", component name, and description information needed to develop business components from business requirements and design drafts.

    ## Constraints
    Basic component materials include:
    - All components in shadcn/ui
    Please note: You should not provide example code and any other text in your response, only provider json response.

    ## Response Format
    You must respond with a JSON object in the following format:
    {
      "componentName": string, // Component name
      "componentDescription": string, // Component description
      "library": [ // Libraries containing required base material components
        {
          "name": string, // Library name
          "components": string[], // Components name in the library
          "description": string // Describe how each component in components is used in a table format
        }
      ]
    }

    ## Workflow
    1. Accept user's business requirements or design draft images
    2. Extract required materials from [Constraints] basic component materials for developing business components
    3. Generate and return the JSON response in the specified format
  • 构建 messages
[
  {
    role: "user",
    content: [
      {
        type: "text",
        text: "Generate a login page",
      },
    ],
  },
];
  • Vercel AI SDK 流式生成组件设计
{
  componentName: "LoginPage",
  componentDescription: "A user authentication page that allows users to sign in to the application with their credentials.",
  library: [
    {
      name: "shadcn/ui",
      components: [
        "Card",
        "Input",
        "Button",
        "Label",
        "Checkbox",
        "Form",
      ],
      description: "| Component | Usage |\n| --- | --- |\n| Card | Container for the login form with Card, CardHeader, CardTitle, CardDescription, and CardContent components |\n| Input | Text fields for email/username and password entry |\n| Button | Submit button for the login form |\n| Label | Text labels for form fields |\n| Checkbox | Remember me option |\n| Form | Form validation and submission handling |",
    },
  ],
  retrievedAugmentationContent: "",
}
生成组件(generateComponent)
  • 构建 system prompt
# You are a senior frontend engineer focused on business component development

    ## Goal
    Generate business component code based on user requirements

    ## Output Specification
    Important: Write the code directly inside each ComponentFile tag. Do NOT use any code block markers (like ```tsx, ```ts, etc.) inside the XML tags.

When modifying existing component code, only return the <ComponentFile> nodes that need to be modified, without returning unchanged files. However, for each modified <ComponentFile> node, you must include the complete code content of that file, even if only a small portion was modified. This ensures the system correctly replaces the entire file content and maintains code integrity.

Output component code in XML format as follows:
<ComponentArtifact name="ComponentName">
<ComponentFile fileName="App.tsx" isEntryFile="true">
import { ComponentName } from './ComponentName';

    const mockProps = {
      // Define mock data here
    };

    export default function App() {
      return <ComponentName {...mockProps} />;
    }

  </ComponentFile>
  
  <ComponentFile fileName="[ComponentName].tsx">
    // Main component implementation
    // Split into multiple files if exceeds 500 lines
    export const ComponentName = () => {
      // Component implementation
    }
  </ComponentFile>

  <ComponentFile fileName="helpers.ts">
    // Helper functions (optional)
  </ComponentFile>

  <ComponentFile fileName="interface.ts">
    // Type definitions for component props
    // All API-interacting data must be defined as props:
    // - initialData for component initialization
    // - onChange, onSave, onDelete etc. for data modifications
  </ComponentFile>
</ComponentArtifact>

    ## Style Specification
    Styles must be written using tailwindcss with full dark/light mode compatibility. Use Tailwind's dark mode utilities (dark:class-name) for theme variants. Prefer using color-scheme-neutral classes or theme-specific variants (dark:bg-gray-800/light:bg-white) to ensure consistent appearance in both dark and light modes.

    ## Component Usage Guidelines

    **Open Source Components**
    - You can use components from shadcn/ui
    - Use the latest stable version of APIs


    ## Additional Rules
    Only use the following npm packages in the generated code: react, react-dom, lucide-react, next/link, next/image, @/lib/utils, framer-motion, react-hook-form, recharts, zod, and components from @/components/ui/* (shadcn base components). Do not import or use any other packages. Specifically, DO NOT use @hookform/resolvers/zod package - instead, manually set up form validation with zod without using this resolver.
  • 构建 messages
[
  {
    role: "user",
    content: [
      {
        type: "text",
        text: "<user-requirements>\n        Generate a login page\n\n        ## Component Design Information\n        - Component Name: LoginPage\n        - Component Description: A user authentication page that allows users to sign in to the application with their credentials.\n        - Base Components Used:\n        \n          shadcn/ui:\n          - Component List: Card, Input, Button, Label, Checkbox, Form\n          - Usage Instructions: | Component | Usage |\n| --- | --- |\n| Card | Container for the login form with Card, CardHeader, CardTitle, CardDescription, and CardContent components |\n| Input | Text fields for email/username and password entry |\n| Button | Submit button for the login form |\n| Label | Text labels for form fields |\n| Checkbox | Remember me option |\n| Form | Form validation and submission handling |\n        \n        </user-requirements>",
      },
    ],
  },
];
  • Vercel AI SDK 流式生成组件代码
I'll create a login page component based on your requirements.

<ComponentArtifact name="LoginPage">
  <ComponentFile fileName="App.tsx" isEntryFile="true">
    import { LoginPage } from './LoginPage';

    export default function App() {
      return (
        <div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 p-4">
          <LoginPage
            onLogin={(data) => console.log('Login attempt:', data)}
            isLoading={false}
            error={null}
          />
        </div>
      );
    }
  </ComponentFile>

  <ComponentFile fileName="LoginPage.tsx">
    import { useState } from "react";
    import { useForm } from "react-hook-form";
    import { z } from "zod";
    import { Eye, EyeOff, Loader2 } from "lucide-react";
    import { Button } from "@/components/ui/button";
    import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
    import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
    import { Input } from "@/components/ui/input";
    import { Checkbox } from "@/components/ui/checkbox";
    import { LoginFormData } from "./interface";

    export const LoginPage = ({
      onLogin,
      isLoading = false,
      error = null,
    }: {
      onLogin: (data: LoginFormData) => void;
      isLoading?: boolean;
      error?: string | null;
    }) => {
      const [showPassword, setShowPassword] = useState(false);

      // Define form validation schema
      const formSchema = z.object({
        email: z.string().email({ message: "Please enter a valid email address" }),
        password: z.string().min(6, { message: "Password must be at least 6 characters" }),
        rememberMe: z.boolean().optional(),
      });

      // Initialize form
      const form = useForm<z.infer<typeof formSchema>>({
        defaultValues: {
          email: "",
          password: "",
          rememberMe: false,
        },
        resolver: (values) => {
          try {
            const validatedData = formSchema.parse(values);
            return { values: validatedData, errors: {} };
          } catch (error) {
            if (error instanceof z.ZodError) {
              const formattedErrors = {};
              error.errors.forEach((err) => {
                const path = err.path.join(".");
                formattedErrors[path] = { message: err.message, type: "validation" };
              });
              return { values: {}, errors: formattedErrors };
            }
            return { values: {}, errors: { "": { message: "Validation failed", type: "validation" } } };
          }
        },
      });

      const onSubmit = (data: z.infer<typeof formSchema>) => {
        onLogin({
          email: data.email,
          password: data.password,
          rememberMe: data.rememberMe || false,
        });
      };

      return (
        <Card className="w-full max-w-md shadow-lg">
          <CardHeader className="space-y-1">
            <CardTitle className="text-2xl font-bold text-center">Login</CardTitle>
            <CardDescription className="text-center">
              Enter your credentials to access your account
            </CardDescription>
          </CardHeader>
          <CardContent>
            {error && (
              <div className="bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-300 p-3 rounded-md mb-4 text-sm">
                {error}
              </div>
            )}
            <Form {...form}>
              <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
                <FormField
                  control={form.control}
                  name="email"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel>Email</FormLabel>
                      <FormControl>
                        <Input
                          placeholder="name@example.com"
                          type="email"
                          autoComplete="email"
                          disabled={isLoading}
                          {...field}
                        />
                      </FormControl>
                      <FormMessage />
                    </FormItem>
                  )}
                />
                <FormField
                  control={form.control}
                  name="password"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel>Password</FormLabel>
                      <FormControl>
                        <div className="relative">
                          <Input
                            placeholder="••••••••"
                            type={showPassword ? "text" : "password"}
                            autoComplete="current-password"
                            disabled={isLoading}
                            {...field}
                          />
                          <Button
                            type="button"
                            variant="ghost"
                            size="sm"
                            className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
                            onClick={() => setShowPassword(!showPassword)}
                            disabled={isLoading}
                          >
                            {showPassword ? (
                              <EyeOff className="h-4 w-4 text-gray-500 dark:text-gray-400" />
                            ) : (
                              <Eye className="h-4 w-4 text-gray-500 dark:text-gray-400" />
                            )}
                            <span className="sr-only">
                              {showPassword ? "Hide password" : "Show password"}
                            </span>
                          </Button>
                        </div>
                      </FormControl>
                      <FormMessage />
                    </FormItem>
                  )}
                />
                <div className="flex items-center space-x-2">
                  <FormField
                    control={form.control}
                    name="rememberMe"
                    render={({ field }) => (
                      <FormItem className="flex flex-row items-center space-x-2 space-y-0">
                        <FormControl>
                          <Checkbox
                            checked={field.value}
                            onCheckedChange={field.onChange}
                            disabled={isLoading}
                          />
                        </FormControl>
                        <FormLabel className="text-sm font-medium leading-none cursor-pointer">
                          Remember me
                        </FormLabel>
                      </FormItem>
                    )}
                  />
                </div>
                <Button type="submit" className="w-full" disabled={isLoading}>
                  {isLoading ? (
                    <>
                      <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                      Signing in...
                    </>
                  ) : (
                    "Sign in"
                  )}
                </Button>
              </form>
            </Form>
          </CardContent>
          <CardFooter className="flex flex-col space-y-4">
            <div className="text-sm text-center text-gray-500 dark:text-gray-400">
              <a
                href="#"
                className="text-primary hover:underline focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 dark:focus:ring-offset-gray-900"
              >
                Forgot your password?
              </a>
            </div>
            <div className="text-sm text-center text-gray-500 dark:text-gray-400">
              Don't have an account?{" "}
              <a
                href="#"
                className="text-primary hover:underline focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 dark:focus:ring-offset-gray-900"
              >
                Sign up
              </a>
            </div>
          </CardFooter>
        </Card>
      );
    };
  </ComponentFile>

  <ComponentFile fileName="interface.ts">
    export interface LoginFormData {
      email: string;
      password: string;
      rememberMe: boolean;
    }
  </ComponentFile>
</ComponentArtifact>
存储组件(storeComponent)
{
  userId: '699fe586a05a78431a7aee91',
  codegenId: '69984e603c8f94846da2b9f5',
  name: 'LoginPage',
  description: 'A user authentication page that allows users to sign in to the application with their credentials.',
  prompt: [ { text: 'Generate a login page', type: 'text' } ],
  code: "I'll create a login page component based on your requirements.\n" +
    '\n' +
    '<ComponentArtifact name="LoginPage">\n' +
    '  <ComponentFile fileName="App.tsx" isEntryFile="true">\n' +
    "    import { LoginPage } from './LoginPage';\n" +
    '    \n' +
    '    export default function App() {\n' +
    '      return (\n' +
    '        <div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 p-4">\n' +
    '          <LoginPage \n' +
    "            onLogin={(data) => console.log('Login attempt:', data)}\n" +
    '            isLoading={false}\n' +
    '            error={null}\n' +
    '          />\n' +
    '        </div>\n' +
    '      );\n' +
    '    }\n' +
    '  </ComponentFile>\n' +
    '  \n' +
    '  <ComponentFile fileName="LoginPage.tsx">\n' +
    '    import { useState } from "react";\n' +
    '    import { useForm } from "react-hook-form";\n' +
    '    import { z } from "zod";\n' +
    '    import { Eye, EyeOff, Loader2 } from "lucide-react";\n' +
    '    import { Button } from "@/components/ui/button";\n' +
    '    import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";\n' +
    '    import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";\n' +
    '    import { Input } from "@/components/ui/input";\n' +
    '    import { Checkbox } from "@/components/ui/checkbox";\n' +
    '    import { LoginFormData } from "./interface";\n' +
    '\n' +
    '    export const LoginPage = ({\n' +
    '      onLogin,\n' +
    '      isLoading = false,\n' +
    '      error = null,\n' +
    '    }: {\n' +
    '      onLogin: (data: LoginFormData) => void;\n' +
    '      isLoading?: boolean;\n' +
    '      error?: string | null;\n' +
    '    }) => {\n' +
    '      const [showPassword, setShowPassword] = useState(false);\n' +
    '      \n' +
    '      // Define form validation schema\n' +
    '      const formSchema = z.object({\n' +
    '        email: z.string().email({ message: "Please enter a valid email address" }),\n' +
    '        password: z.string().min(6, { message: "Password must be at least 6 characters" }),\n' +
    '        rememberMe: z.boolean().optional(),\n' +
    '      });\n' +
    '\n' +
    '      // Initialize form\n' +
    '      const form = useForm<z.infer<typeof formSchema>>({\n' +
    '        defaultValues: {\n' +
    '          email: "",\n' +
    '          password: "",\n' +
    '          rememberMe: false,\n' +
    '        },\n' +
    '        resolver: (values) => {\n' +
    '          try {\n' +
    '            const validatedData = formSchema.parse(values);\n' +
    '            return { values: validatedData, errors: {} };\n' +
    '          } catch (error) {\n' +
    '            if (error instanceof z.ZodError) {\n' +
    '              const formattedErrors = {};\n' +
    '              error.errors.forEach((err) => {\n' +
    '                const path = err.path.join(".");\n' +
    '                formattedErrors[path] = { message: err.message, type: "validation" };\n' +
    '              });\n' +
    '              return { values: {}, errors: formattedErrors };\n' +
    '            }\n' +
    '            return { values: {}, errors: { "": { message: "Validation failed", type: "validation" } } };\n' +
    '          }\n' +
    '        },\n' +
    '      });\n' +
    '\n' +
    '      const onSubmit = (data: z.infer<typeof formSchema>) => {\n' +
    '        onLogin({\n' +
    '          email: data.email,\n' +
    '          password: data.password,\n' +
    '          rememberMe: data.rememberMe || false,\n' +
    '        });\n' +
    '      };\n' +
    '\n' +
    '      return (\n' +
    '        <Card className="w-full max-w-md shadow-lg">\n' +
    '          <CardHeader className="space-y-1">\n' +
    '            <CardTitle className="text-2xl font-bold text-center">Login</CardTitle>\n' +
    '            <CardDescription className="text-center">\n' +
    '              Enter your credentials to access your account\n' +
    '            </CardDescription>\n' +
    '          </CardHeader>\n' +
    '          <CardContent>\n' +
    '            {error && (\n' +
    '              <div className="bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-300 p-3 rounded-md mb-4 text-sm">\n' +
    '                {error}\n' +
    '              </div>\n' +
    '            )}\n' +
    '            <Form {...form}>\n' +
    '              <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">\n' +
    '                <FormField\n' +
    '                  control={form.control}\n' +
    '                  name="email"\n' +
    '                  render={({ field }) => (\n' +
    '                    <FormItem>\n' +
    '                      <FormLabel>Email</FormLabel>\n' +
    '                      <FormControl>\n' +
    '                        <Input \n' +
    '                          placeholder="name@example.com" \n' +
    '                          type="email" \n' +
    '                          autoComplete="email"\n' +
    '                          disabled={isLoading}\n' +
    '                          {...field} \n' +
    '                        />\n' +
    '                      </FormControl>\n' +
    '                      <FormMessage />\n' +
    '                    </FormItem>\n' +
    '                  )}\n' +
    '                />\n' +
    '                <FormField\n' +
    '                  control={form.control}\n' +
    '                  name="password"\n' +
    '                  render={({ field }) => (\n' +
    '                    <FormItem>\n' +
    '                      <FormLabel>Password</FormLabel>\n' +
    '                      <FormControl>\n' +
    '                        <div className="relative">\n' +
    '                          <Input\n' +
    '                            placeholder="••••••••"\n' +
    '                            type={showPassword ? "text" : "password"}\n' +
    '                            autoComplete="current-password"\n' +
    '                            disabled={isLoading}\n' +
    '                            {...field}\n' +
    '                          />\n' +
    '                          <Button\n' +
    '                            type="button"\n' +
    '                            variant="ghost"\n' +
    '                            size="sm"\n' +
    '                            className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"\n' +
    '                            onClick={() => setShowPassword(!showPassword)}\n' +
    '                            disabled={isLoading}\n' +
    '                          >\n' +
    '                            {showPassword ? (\n' +
    '                              <EyeOff className="h-4 w-4 text-gray-500 dark:text-gray-400" />\n' +
    '                            ) : (\n' +
    '                              <Eye className="h-4 w-4 text-gray-500 dark:text-gray-400" />\n' +
    '                            )}\n' +
    '                            <span className="sr-only">\n' +
    '                              {showPassword ? "Hide password" : "Show password"}\n' +
    '                            </span>\n' +
    '                          </Button>\n' +
    '                        </div>\n' +
    '                      </FormControl>\n' +
    '                      <FormMessage />\n' +
    '                    </FormItem>\n' +
    '                  )}\n' +
    '                />\n' +
    '                <div className="flex items-center space-x-2">\n' +
    '                  <FormField\n' +
    '                    control={form.control}\n' +
    '                    name="rememberMe"\n' +
    '                    render={({ field }) => (\n' +
    '                      <FormItem className="flex flex-row items-center space-x-2 space-y-0">\n' +
    '                        <FormControl>\n' +
    '                          <Checkbox\n' +
    '                            checked={field.value}\n' +
    '                            onCheckedChange={field.onChange}\n' +
    '                            disabled={isLoading}\n' +
    '                          />\n' +
    '                        </FormControl>\n' +
    '                        <FormLabel className="text-sm font-medium leading-none cursor-pointer">\n' +
    '                          Remember me\n' +
    '                        </FormLabel>\n' +
    '                      </FormItem>\n' +
    '                    )}\n' +
    '                  />\n' +
    '                </div>\n' +
    '                <Button type="submit" className="w-full" disabled={isLoading}>\n' +
    '                  {isLoading ? (\n' +
    '                    <>\n' +
    '                      <Loader2 className="mr-2 h-4 w-4 animate-spin" />\n' +
    '                      Signing in...\n' +
    '                    </>\n' +
    '                  ) : (\n' +
    '                    "Sign in"\n' +
    '                  )}\n' +
    '                </Button>\n' +
    '              </form>\n' +
    '            </Form>\n' +
    '          </CardContent>\n' +
    '          <CardFooter className="flex flex-col space-y-4">\n' +
    '            <div className="text-sm text-center text-gray-500 dark:text-gray-400">\n' +
    '              <a \n' +
    '                href="#" \n' +
    '                className="text-primary hover:underline focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 dark:focus:ring-offset-gray-900"\n' +
    '              >\n' +
    '                Forgot your password?\n' +
    '              </a>\n' +
    '            </div>\n' +
    '            <div className="text-sm text-center text-gray-500 dark:text-gray-400">\n' +
    `              Don't have an account?{" "}\n` +
    '              <a \n' +
    '                href="#" \n' +
    '                className="text-primary hover:underline focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 dark:focus:ring-offset-gray-900"\n' +
    '              >\n' +
    '                Sign up\n' +
    '              </a>\n' +
    '            </div>\n' +
    '          </CardFooter>\n' +
    '        </Card>\n' +
    '      );\n' +
    '    };\n' +
    '  </ComponentFile>\n' +
    '\n' +
    '  <ComponentFile fileName="interface.ts">\n' +
    '    export interface LoginFormData {\n' +
    '      email: string;\n' +
    '      password: string;\n' +
    '      rememberMe: boolean;\n' +
    '    }\n' +
    '  </ComponentFile>\n' +
    '</ComponentArtifact>'
}
生成效果
AI 迭代组件
设计组件(designComponent)
  • 构建 system prompt
# You are a senior frontend engineer who excels at developing business components.

    ## Goal
    Extract the "basic component materials", component name, and description information needed to develop business components from business requirements and design drafts.

    ## Constraints
    Basic component materials include:
    - All components in shadcn/ui
    Please note: You should not provide example code and any other text in your response, only provider json response.

    ## Response Format
    You must respond with a JSON object in the following format:
    {
      "componentName": string, // Component name
      "componentDescription": string, // Component description
      "library": [ // Libraries containing required base material components
        {
          "name": string, // Library name
          "components": string[], // Components name in the library
          "description": string // Describe how each component in components is used in a table format
        }
      ]
    }

    ## Workflow
    1. Accept user's business requirements or design draft images
    2. Extract required materials from [Constraints] basic component materials for developing business components
    3. Generate and return the JSON response in the specified format
  • 构建 messages
[
  {
    role: "user",
    content: [
      {
        type: "text",
        text: "Generate a login page",
      },
    ],
  },
  {
    role: "assistant",
    content:
      '\n        - Component name: LoginPage\n        - Component code:\n        I\'ll create a login page component based on your requirements.\n\n<ComponentArtifact name="LoginPage">\n  <ComponentFile fileName="App.tsx" isEntryFile="true">\n    import { LoginPage } from \'./LoginPage\';\n    \n    export default function App() {\n      return (\n        <div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 p-4">\n          <LoginPage \n            onLogin={(data) => console.log(\'Login attempt:\', data)}\n            isLoading={false}\n            error={null}\n          />\n        </div>\n      );\n    }\n  </ComponentFile>\n  \n  <ComponentFile fileName="LoginPage.tsx">\n    import { useState } from "react";\n    import { useForm } from "react-hook-form";\n    import { z } from "zod";\n    import { Eye, EyeOff, Loader2 } from "lucide-react";\n    import { Button } from "@/components/ui/button";\n    import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";\n    import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";\n    import { Input } from "@/components/ui/input";\n    import { Checkbox } from "@/components/ui/checkbox";\n    import { LoginFormData } from "./interface";\n\n    export const LoginPage = ({\n      onLogin,\n      isLoading = false,\n      error = null,\n    }: {\n      onLogin: (data: LoginFormData) => void;\n      isLoading?: boolean;\n      error?: string | null;\n    }) => {\n      const [showPassword, setShowPassword] = useState(false);\n      \n      // Define form validation schema\n      const formSchema = z.object({\n        email: z.string().email({ message: "Please enter a valid email address" }),\n        password: z.string().min(6, { message: "Password must be at least 6 characters" }),\n        rememberMe: z.boolean().optional(),\n      });\n\n      // Initialize form\n      const form = useForm<z.infer<typeof formSchema>>({\n        defaultValues: {\n          email: "",\n          password: "",\n          rememberMe: false,\n        },\n        resolver: (values) => {\n          try {\n            const validatedData = formSchema.parse(values);\n            return { values: validatedData, errors: {} };\n          } catch (error) {\n            if (error instanceof z.ZodError) {\n              const formattedErrors = {};\n              error.errors.forEach((err) => {\n                const path = err.path.join(".");\n                formattedErrors[path] = { message: err.message, type: "validation" };\n              });\n              return { values: {}, errors: formattedErrors };\n            }\n            return { values: {}, errors: { "": { message: "Validation failed", type: "validation" } } };\n          }\n        },\n      });\n\n      const onSubmit = (data: z.infer<typeof formSchema>) => {\n        onLogin({\n          email: data.email,\n          password: data.password,\n          rememberMe: data.rememberMe || false,\n        });\n      };\n\n      return (\n        <Card className="w-full max-w-md shadow-lg">\n          <CardHeader className="space-y-1">\n            <CardTitle className="text-2xl font-bold text-center">Login</CardTitle>\n            <CardDescription className="text-center">\n              Enter your credentials to access your account\n            </CardDescription>\n          </CardHeader>\n          <CardContent>\n            {error && (\n              <div className="bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-300 p-3 rounded-md mb-4 text-sm">\n                {error}\n              </div>\n            )}\n            <Form {...form}>\n              <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">\n                <FormField\n                  control={form.control}\n                  name="email"\n                  render={({ field }) => (\n                    <FormItem>\n                      <FormLabel>Email</FormLabel>\n                      <FormControl>\n                        <Input \n                          placeholder="name@example.com" \n                          type="email" \n                          autoComplete="email"\n                          disabled={isLoading}\n                          {...field} \n                        />\n                      </FormControl>\n                      <FormMessage />\n                    </FormItem>\n                  )}\n                />\n                <FormField\n                  control={form.control}\n                  name="password"\n                  render={({ field }) => (\n                    <FormItem>\n                      <FormLabel>Password</FormLabel>\n                      <FormControl>\n                        <div className="relative">\n                          <Input\n                            placeholder="••••••••"\n                            type={showPassword ? "text" : "password"}\n                            autoComplete="current-password"\n                            disabled={isLoading}\n                            {...field}\n                          />\n                          <Button\n                            type="button"\n                            variant="ghost"\n                            size="sm"\n                            className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"\n                            onClick={() => setShowPassword(!showPassword)}\n                            disabled={isLoading}\n                          >\n                            {showPassword ? (\n                              <EyeOff className="h-4 w-4 text-gray-500 dark:text-gray-400" />\n                            ) : (\n                              <Eye className="h-4 w-4 text-gray-500 dark:text-gray-400" />\n                            )}\n                            <span className="sr-only">\n                              {showPassword ? "Hide password" : "Show password"}\n                            </span>\n                          </Button>\n                        </div>\n                      </FormControl>\n                      <FormMessage />\n                    </FormItem>\n                  )}\n                />\n                <div className="flex items-center space-x-2">\n                  <FormField\n                    control={form.control}\n                    name="rememberMe"\n                    render={({ field }) => (\n                      <FormItem className="flex flex-row items-center space-x-2 space-y-0">\n                        <FormControl>\n                          <Checkbox\n                            checked={field.value}\n                            onCheckedChange={field.onChange}\n                            disabled={isLoading}\n                          />\n                        </FormControl>\n                        <FormLabel className="text-sm font-medium leading-none cursor-pointer">\n                          Remember me\n                        </FormLabel>\n                      </FormItem>\n                    )}\n                  />\n                </div>\n                <Button type="submit" className="w-full" disabled={isLoading}>\n                  {isLoading ? (\n                    <>\n                      <Loader2 className="mr-2 h-4 w-4 animate-spin" />\n                      Signing in...\n                    </>\n                  ) : (\n                    "Sign in"\n                  )}\n                </Button>\n              </form>\n            </Form>\n          </CardContent>\n          <CardFooter className="flex flex-col space-y-4">\n            <div className="text-sm text-center text-gray-500 dark:text-gray-400">\n              <a \n                href="#" \n                className="text-primary hover:underline focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 dark:focus:ring-offset-gray-900"\n              >\n                Forgot your password?\n              </a>\n            </div>\n            <div className="text-sm text-center text-gray-500 dark:text-gray-400">\n              Don\'t have an account?{" "}\n              <a \n                href="#" \n                className="text-primary hover:underline focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 dark:focus:ring-offset-gray-900"\n              >\n                Sign up\n              </a>\n            </div>\n          </CardFooter>\n        </Card>\n      );\n    };\n  </ComponentFile>\n\n  <ComponentFile fileName="interface.ts">\n    export interface LoginFormData {\n      email: string;\n      password: string;\n      rememberMe: boolean;\n    }\n  </ComponentFile>\n</ComponentArtifact>\n      ',
  },
  {
    role: "user",
    content: [
      {
        type: "text",
        text: "帮我调整下样式",
      },
    ],
  },
];
  • Vercel AI SDK 流式生成组件设计
{
  componentName: "LoginPage",
  componentDescription: "A responsive login page component with email and password fields, remember me option, and links for password recovery and signup",
  library: [
    {
      name: "shadcn/ui",
      components: [
        "Card",
        "Button",
        "Input",
        "Form",
        "Checkbox",
      ],
      description: "| Component | Usage |\n|-----------|-------|\n| Card | Used as the container for the login form with CardHeader, CardContent, and CardFooter for structured layout |\n| Button | Used for the submit button and password visibility toggle |\n| Input | Used for email and password input fields |\n| Form | Used for form validation and handling with FormField, FormItem, FormLabel, FormControl, and FormMessage |\n| Checkbox | Used for the 'Remember me' option |",
    },
  ],
  retrievedAugmentationContent: "",
}
生成组件(generateComponent)
  • 构建 system prompt
# You are a senior frontend engineer focused on business component development

    ## Goal
    Generate business component code based on user requirements

    ## Output Specification
    Important: Write the code directly inside each ComponentFile tag. Do NOT use any code block markers (like ```tsx, ```ts, etc.) inside the XML tags.

When modifying existing component code, only return the <ComponentFile> nodes that need to be modified, without returning unchanged files. However, for each modified <ComponentFile> node, you must include the complete code content of that file, even if only a small portion was modified. This ensures the system correctly replaces the entire file content and maintains code integrity.

Output component code in XML format as follows:
<ComponentArtifact name="ComponentName">
<ComponentFile fileName="App.tsx" isEntryFile="true">
import { ComponentName } from './ComponentName';

    const mockProps = {
      // Define mock data here
    };

    export default function App() {
      return <ComponentName {...mockProps} />;
    }

  </ComponentFile>
  
  <ComponentFile fileName="[ComponentName].tsx">
    // Main component implementation
    // Split into multiple files if exceeds 500 lines
    export const ComponentName = () => {
      // Component implementation
    }
  </ComponentFile>

  <ComponentFile fileName="helpers.ts">
    // Helper functions (optional)
  </ComponentFile>

  <ComponentFile fileName="interface.ts">
    // Type definitions for component props
    // All API-interacting data must be defined as props:
    // - initialData for component initialization
    // - onChange, onSave, onDelete etc. for data modifications
  </ComponentFile>
</ComponentArtifact>

    ## Style Specification
    Styles must be written using tailwindcss with full dark/light mode compatibility. Use Tailwind's dark mode utilities (dark:class-name) for theme variants. Prefer using color-scheme-neutral classes or theme-specific variants (dark:bg-gray-800/light:bg-white) to ensure consistent appearance in both dark and light modes.

    ## Component Usage Guidelines

    **Open Source Components**
    - You can use components from shadcn/ui
    - Use the latest stable version of APIs


    ## Additional Rules
    Only use the following npm packages in the generated code: react, react-dom, lucide-react, next/link, next/image, @/lib/utils, framer-motion, react-hook-form, recharts, zod, and components from @/components/ui/* (shadcn base components). Do not import or use any other packages. Specifically, DO NOT use @hookform/resolvers/zod package - instead, manually set up form validation with zod without using this resolver.
  • 构建 messages
[
  {
    role: "user",
    content: [
      {
        type: "text",
        text: "Generate a login page",
      },
    ],
  },
  {
    role: "assistant",
    content:
      '\n          - Component name: LoginPage\n          - Component code:\n          I\'ll create a login page component based on your requirements.\n\n<ComponentArtifact name="LoginPage">\n  <ComponentFile fileName="App.tsx" isEntryFile="true">\n    import { LoginPage } from \'./LoginPage\';\n    \n    export default function App() {\n      return (\n        <div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 p-4">\n          <LoginPage \n            onLogin={(data) => console.log(\'Login attempt:\', data)}\n            isLoading={false}\n            error={null}\n          />\n        </div>\n      );\n    }\n  </ComponentFile>\n  \n  <ComponentFile fileName="LoginPage.tsx">\n    import { useState } from "react";\n    import { useForm } from "react-hook-form";\n    import { z } from "zod";\n    import { Eye, EyeOff, Loader2 } from "lucide-react";\n    import { Button } from "@/components/ui/button";\n    import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";\n    import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";\n    import { Input } from "@/components/ui/input";\n    import { Checkbox } from "@/components/ui/checkbox";\n    import { LoginFormData } from "./interface";\n\n    export const LoginPage = ({\n      onLogin,\n      isLoading = false,\n      error = null,\n    }: {\n      onLogin: (data: LoginFormData) => void;\n      isLoading?: boolean;\n      error?: string | null;\n    }) => {\n      const [showPassword, setShowPassword] = useState(false);\n      \n      // Define form validation schema\n      const formSchema = z.object({\n        email: z.string().email({ message: "Please enter a valid email address" }),\n        password: z.string().min(6, { message: "Password must be at least 6 characters" }),\n        rememberMe: z.boolean().optional(),\n      });\n\n      // Initialize form\n      const form = useForm<z.infer<typeof formSchema>>({\n        defaultValues: {\n          email: "",\n          password: "",\n          rememberMe: false,\n        },\n        resolver: (values) => {\n          try {\n            const validatedData = formSchema.parse(values);\n            return { values: validatedData, errors: {} };\n          } catch (error) {\n            if (error instanceof z.ZodError) {\n              const formattedErrors = {};\n              error.errors.forEach((err) => {\n                const path = err.path.join(".");\n                formattedErrors[path] = { message: err.message, type: "validation" };\n              });\n              return { values: {}, errors: formattedErrors };\n            }\n            return { values: {}, errors: { "": { message: "Validation failed", type: "validation" } } };\n          }\n        },\n      });\n\n      const onSubmit = (data: z.infer<typeof formSchema>) => {\n        onLogin({\n          email: data.email,\n          password: data.password,\n          rememberMe: data.rememberMe || false,\n        });\n      };\n\n      return (\n        <Card className="w-full max-w-md shadow-lg">\n          <CardHeader className="space-y-1">\n            <CardTitle className="text-2xl font-bold text-center">Login</CardTitle>\n            <CardDescription className="text-center">\n              Enter your credentials to access your account\n            </CardDescription>\n          </CardHeader>\n          <CardContent>\n            {error && (\n              <div className="bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-300 p-3 rounded-md mb-4 text-sm">\n                {error}\n              </div>\n            )}\n            <Form {...form}>\n              <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">\n                <FormField\n                  control={form.control}\n                  name="email"\n                  render={({ field }) => (\n                    <FormItem>\n                      <FormLabel>Email</FormLabel>\n                      <FormControl>\n                        <Input \n                          placeholder="name@example.com" \n                          type="email" \n                          autoComplete="email"\n                          disabled={isLoading}\n                          {...field} \n                        />\n                      </FormControl>\n                      <FormMessage />\n                    </FormItem>\n                  )}\n                />\n                <FormField\n                  control={form.control}\n                  name="password"\n                  render={({ field }) => (\n                    <FormItem>\n                      <FormLabel>Password</FormLabel>\n                      <FormControl>\n                        <div className="relative">\n                          <Input\n                            placeholder="••••••••"\n                            type={showPassword ? "text" : "password"}\n                            autoComplete="current-password"\n                            disabled={isLoading}\n                            {...field}\n                          />\n                          <Button\n                            type="button"\n                            variant="ghost"\n                            size="sm"\n                            className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"\n                            onClick={() => setShowPassword(!showPassword)}\n                            disabled={isLoading}\n                          >\n                            {showPassword ? (\n                              <EyeOff className="h-4 w-4 text-gray-500 dark:text-gray-400" />\n                            ) : (\n                              <Eye className="h-4 w-4 text-gray-500 dark:text-gray-400" />\n                            )}\n                            <span className="sr-only">\n                              {showPassword ? "Hide password" : "Show password"}\n                            </span>\n                          </Button>\n                        </div>\n                      </FormControl>\n                      <FormMessage />\n                    </FormItem>\n                  )}\n                />\n                <div className="flex items-center space-x-2">\n                  <FormField\n                    control={form.control}\n                    name="rememberMe"\n                    render={({ field }) => (\n                      <FormItem className="flex flex-row items-center space-x-2 space-y-0">\n                        <FormControl>\n                          <Checkbox\n                            checked={field.value}\n                            onCheckedChange={field.onChange}\n                            disabled={isLoading}\n                          />\n                        </FormControl>\n                        <FormLabel className="text-sm font-medium leading-none cursor-pointer">\n                          Remember me\n                        </FormLabel>\n                      </FormItem>\n                    )}\n                  />\n                </div>\n                <Button type="submit" className="w-full" disabled={isLoading}>\n                  {isLoading ? (\n                    <>\n                      <Loader2 className="mr-2 h-4 w-4 animate-spin" />\n                      Signing in...\n                    </>\n                  ) : (\n                    "Sign in"\n                  )}\n                </Button>\n              </form>\n            </Form>\n          </CardContent>\n          <CardFooter className="flex flex-col space-y-4">\n            <div className="text-sm text-center text-gray-500 dark:text-gray-400">\n              <a \n                href="#" \n                className="text-primary hover:underline focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 dark:focus:ring-offset-gray-900"\n              >\n                Forgot your password?\n              </a>\n            </div>\n            <div className="text-sm text-center text-gray-500 dark:text-gray-400">\n              Don\'t have an account?{" "}\n              <a \n                href="#" \n                className="text-primary hover:underline focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 dark:focus:ring-offset-gray-900"\n              >\n                Sign up\n              </a>\n            </div>\n          </CardFooter>\n        </Card>\n      );\n    };\n  </ComponentFile>\n\n  <ComponentFile fileName="interface.ts">\n    export interface LoginFormData {\n      email: string;\n      password: string;\n      rememberMe: boolean;\n    }\n  </ComponentFile>\n</ComponentArtifact>\n        ',
  },
  {
    role: "user",
    content: [
      {
        type: "text",
        text: "<user-requirements>\n        帮我调整下样式\n\n        ## Component Design Information\n        - Component Name: LoginPage\n        - Component Description: A responsive login page component with email and password fields, remember me option, and links for password recovery and signup\n        - Base Components Used:\n        \n          shadcn/ui:\n          - Component List: Card, Button, Input, Form, Checkbox\n          - Usage Instructions: | Component | Usage |\n|-----------|-------|\n| Card | Used as the container for the login form with CardHeader, CardContent, and CardFooter for structured layout |\n| Button | Used for the submit button and password visibility toggle |\n| Input | Used for email and password input fields |\n| Form | Used for form validation and handling with FormField, FormItem, FormLabel, FormControl, and FormMessage |\n| Checkbox | Used for the 'Remember me' option |\n        \n        </user-requirements>",
      },
    ],
  },
];
  • Vercel AI SDK 流式生成组件代码
I'll adjust the styling of the LoginPage component to enhance its appearance while maintaining its functionality.

<ComponentArtifact name="LoginPage">
  <ComponentFile fileName="LoginPage.tsx">
    import { useState } from "react";
    import { useForm } from "react-hook-form";
    import { z } from "zod";
    import { Eye, EyeOff, Loader2 } from "lucide-react";
    import { Button } from "@/components/ui/button";
    import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
    import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
    import { Input } from "@/components/ui/input";
    import { Checkbox } from "@/components/ui/checkbox";
    import { LoginFormData } from "./interface";

    export const LoginPage = ({
      onLogin,
      isLoading = false,
      error = null,
    }: {
      onLogin: (data: LoginFormData) => void;
      isLoading?: boolean;
      error?: string | null;
    }) => {
      const [showPassword, setShowPassword] = useState(false);

      // Define form validation schema
      const formSchema = z.object({
        email: z.string().email({ message: "Please enter a valid email address" }),
        password: z.string().min(6, { message: "Password must be at least 6 characters" }),
        rememberMe: z.boolean().optional(),
      });

      // Initialize form
      const form = useForm<z.infer<typeof formSchema>>({
        defaultValues: {
          email: "",
          password: "",
          rememberMe: false,
        },
        resolver: (values) => {
          try {
            const validatedData = formSchema.parse(values);
            return { values: validatedData, errors: {} };
          } catch (error) {
            if (error instanceof z.ZodError) {
              const formattedErrors = {};
              error.errors.forEach((err) => {
                const path = err.path.join(".");
                formattedErrors[path] = { message: err.message, type: "validation" };
              });
              return { values: {}, errors: formattedErrors };
            }
            return { values: {}, errors: { "": { message: "Validation failed", type: "validation" } } };
          }
        },
      });

      const onSubmit = (data: z.infer<typeof formSchema>) => {
        onLogin({
          email: data.email,
          password: data.password,
          rememberMe: data.rememberMe || false,
        });
      };

      return (
        <Card className="w-full max-w-md shadow-xl border-0 dark:border dark:border-gray-800 bg-white dark:bg-gray-950 rounded-xl overflow-hidden">
          <div className="h-2 bg-gradient-to-r from-blue-500 to-purple-600"></div>
          <CardHeader className="space-y-2 pt-6">
            <CardTitle className="text-2xl font-bold text-center text-gray-900 dark:text-gray-100">Welcome Back</CardTitle>
            <CardDescription className="text-center text-gray-500 dark:text-gray-400">
              Sign in to your account to continue
            </CardDescription>
          </CardHeader>
          <CardContent className="px-6 py-4">
            {error && (
              <div className="bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-300 p-3 rounded-lg mb-5 text-sm border border-red-200 dark:border-red-800/30">
                <p className="flex items-center">
                  <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
                  </svg>
                  {error}
                </p>
              </div>
            )}
            <Form {...form}>
              <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-5">
                <FormField
                  control={form.control}
                  name="email"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel className="text-gray-700 dark:text-gray-300">Email</FormLabel>
                      <FormControl>
                        <Input
                          placeholder="name@example.com"
                          type="email"
                          autoComplete="email"
                          disabled={isLoading}
                          className="h-11 bg-gray-50 dark:bg-gray-900 border-gray-200 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-600"
                          {...field}
                        />
                      </FormControl>
                      <FormMessage className="text-xs font-medium" />
                    </FormItem>
                  )}
                />
                <FormField
                  control={form.control}
                  name="password"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel className="text-gray-700 dark:text-gray-300">Password</FormLabel>
                      <FormControl>
                        <div className="relative">
                          <Input
                            placeholder="••••••••"
                            type={showPassword ? "text" : "password"}
                            autoComplete="current-password"
                            disabled={isLoading}
                            className="h-11 bg-gray-50 dark:bg-gray-900 border-gray-200 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-600 pr-10"
                            {...field}
                          />
                          <Button
                            type="button"
                            variant="ghost"
                            size="sm"
                            className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent text-gray-500 dark:text-gray-400"
                            onClick={() => setShowPassword(!showPassword)}
                            disabled={isLoading}
                          >
                            {showPassword ? (
                              <EyeOff className="h-4 w-4" />
                            ) : (
                              <Eye className="h-4 w-4" />
                            )}
                            <span className="sr-only">
                              {showPassword ? "Hide password" : "Show password"}
                            </span>
                          </Button>
                        </div>
                      </FormControl>
                      <FormMessage className="text-xs font-medium" />
                    </FormItem>
                  )}
                />
                <div className="flex items-center justify-between">
                  <FormField
                    control={form.control}
                    name="rememberMe"
                    render={({ field }) => (
                      <FormItem className="flex flex-row items-center space-x-2 space-y-0">
                        <FormControl>
                          <Checkbox
                            checked={field.value}
                            onCheckedChange={field.onChange}
                            disabled={isLoading}
                            className="text-blue-600 dark:text-blue-500 border-gray-300 dark:border-gray-600 rounded"
                          />
                        </FormControl>
                        <FormLabel className="text-sm font-medium leading-none cursor-pointer text-gray-600 dark:text-gray-400">
                          Remember me
                        </FormLabel>
                      </FormItem>
                    )}
                  />
                  <a
                    href="#"
                    className="text-sm font-medium text-blue-600 hover:text-blue-500 dark:text-blue-500 dark:hover:text-blue-400 hover:underline focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900"
                  >
                    Forgot password?
                  </a>
                </div>
                <Button
                  type="submit"
                  className="w-full h-11 bg-gradient-to-r from-blue-600 to-blue-500 hover:from-blue-700 hover:to-blue-600 dark:from-blue-600 dark:to-blue-500 dark:hover:from-blue-700 dark:hover:to-blue-600 text-white font-medium rounded-lg transition-all duration-200 shadow-md hover:shadow-lg"
                  disabled={isLoading}
                >
                  {isLoading ? (
                    <>
                      <Loader2 className="mr-2 h-4 w-4 animate-spin" />
                      Signing in...
                    </>
                  ) : (
                    "Sign in"
                  )}
                </Button>
              </form>
            </Form>
          </CardContent>
          <CardFooter className="flex flex-col space-y-4 px-6 py-5 bg-gray-50 dark:bg-gray-900/50 border-t border-gray-100 dark:border-gray-800">
            <div className="text-sm text-center text-gray-600 dark:text-gray-400">
              Don't have an account?{" "}
              <a
                href="#"
                className="font-medium text-blue-600 hover:text-blue-500 dark:text-blue-500 dark:hover:text-blue-400 hover:underline focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900"
              >
                Create an account
              </a>
            </div>
          </CardFooter>
        </Card>
      );
    };
  </ComponentFile>
</ComponentArtifact>
存储组件(storeComponent)
{
  id: '69a038076b570a36e1ecc6cb',
  prompt: [ { type: 'text', text: '帮我调整下样式' } ],
  code: '<ComponentArtifact name="LoginPage">\n' +
    '  <ComponentFile fileName="App.tsx" isEntryFile="true">\n' +
    "    import { LoginPage } from './LoginPage';\n" +
    '    \n' +
    '    export default function App() {\n' +
    '      return (\n' +
    '        <div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 p-4">\n' +
    '          <LoginPage \n' +
    "            onLogin={(data) => console.log('Login attempt:', data)}\n" +
    '            isLoading={false}\n' +
    '            error={null}\n' +
    '          />\n' +
    '        </div>\n' +
    '      );\n' +
    '    }\n' +
    '  </ComponentFile>\n' +
    '  <ComponentFile fileName="LoginPage.tsx" isEntryFile="false">\n' +
    '    import { useState } from "react";\n' +
    '    import { useForm } from "react-hook-form";\n' +
    '    import { z } from "zod";\n' +
    '    import { Eye, EyeOff, Loader2 } from "lucide-react";\n' +
    '    import { Button } from "@/components/ui/button";\n' +
    '    import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";\n' +
    '    import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";\n' +
    '    import { Input } from "@/components/ui/input";\n' +
    '    import { Checkbox } from "@/components/ui/checkbox";\n' +
    '    import { LoginFormData } from "./interface";\n' +
    '\n' +
    '    export const LoginPage = ({\n' +
    '      onLogin,\n' +
    '      isLoading = false,\n' +
    '      error = null,\n' +
    '    }: {\n' +
    '      onLogin: (data: LoginFormData) => void;\n' +
    '      isLoading?: boolean;\n' +
    '      error?: string | null;\n' +
    '    }) => {\n' +
    '      const [showPassword, setShowPassword] = useState(false);\n' +
    '      \n' +
    '      // Define form validation schema\n' +
    '      const formSchema = z.object({\n' +
    '        email: z.string().email({ message: "Please enter a valid email address" }),\n' +
    '        password: z.string().min(6, { message: "Password must be at least 6 characters" }),\n' +
    '        rememberMe: z.boolean().optional(),\n' +
    '      });\n' +
    '\n' +
    '      // Initialize form\n' +
    '      const form = useForm<z.infer<typeof formSchema>>({\n' +
    '        defaultValues: {\n' +
    '          email: "",\n' +
    '          password: "",\n' +
    '          rememberMe: false,\n' +
    '        },\n' +
    '        resolver: (values) => {\n' +
    '          try {\n' +
    '            const validatedData = formSchema.parse(values);\n' +
    '            return { values: validatedData, errors: {} };\n' +
    '          } catch (error) {\n' +
    '            if (error instanceof z.ZodError) {\n' +
    '              const formattedErrors = {};\n' +
    '              error.errors.forEach((err) => {\n' +
    '                const path = err.path.join(".");\n' +
    '                formattedErrors[path] = { message: err.message, type: "validation" };\n' +
    '              });\n' +
    '              return { values: {}, errors: formattedErrors };\n' +
    '            }\n' +
    '            return { values: {}, errors: { "": { message: "Validation failed", type: "validation" } } };\n' +
    '          }\n' +
    '        },\n' +
    '      });\n' +
    '\n' +
    '      const onSubmit = (data: z.infer<typeof formSchema>) => {\n' +
    '        onLogin({\n' +
    '          email: data.email,\n' +
    '          password: data.password,\n' +
    '          rememberMe: data.rememberMe || false,\n' +
    '        });\n' +
    '      };\n' +
    '\n' +
    '      return (\n' +
    '        <Card className="w-full max-w-md shadow-xl border-0 dark:border dark:border-gray-800 bg-white dark:bg-gray-950 rounded-xl overflow-hidden">\n' +
    '          <div className="h-2 bg-gradient-to-r from-blue-500 to-purple-600"></div>\n' +
    '          <CardHeader className="space-y-2 pt-6">\n' +
    '            <CardTitle className="text-2xl font-bold text-center text-gray-900 dark:text-gray-100">Welcome Back</CardTitle>\n' +
    '            <CardDescription className="text-center text-gray-500 dark:text-gray-400">\n' +
    '              Sign in to your account to continue\n' +
    '            </CardDescription>\n' +
    '          </CardHeader>\n' +
    '          <CardContent className="px-6 py-4">\n' +
    '            {error && (\n' +
    '              <div className="bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-300 p-3 rounded-lg mb-5 text-sm border border-red-200 dark:border-red-800/30">\n' +
    '                <p className="flex items-center">\n' +
    '                  <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">\n' +
    '                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />\n' +
    '                  </svg>\n' +
    '                  {error}\n' +
    '                </p>\n' +
    '              </div>\n' +
    '            )}\n' +
    '            <Form {...form}>\n' +
    '              <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-5">\n' +
    '                <FormField\n' +
    '                  control={form.control}\n' +
    '                  name="email"\n' +
    '                  render={({ field }) => (\n' +
    '                    <FormItem>\n' +
    '                      <FormLabel className="text-gray-700 dark:text-gray-300">Email</FormLabel>\n' +
    '                      <FormControl>\n' +
    '                        <Input \n' +
    '                          placeholder="name@example.com" \n' +
    '                          type="email" \n' +
    '                          autoComplete="email"\n' +
    '                          disabled={isLoading}\n' +
    '                          className="h-11 bg-gray-50 dark:bg-gray-900 border-gray-200 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-600"\n' +
    '                          {...field} \n' +
    '                        />\n' +
    '                      </FormControl>\n' +
    '                      <FormMessage className="text-xs font-medium" />\n' +
    '                    </FormItem>\n' +
    '                  )}\n' +
    '                />\n' +
    '                <FormField\n' +
    '                  control={form.control}\n' +
    '                  name="password"\n' +
    '                  render={({ field }) => (\n' +
    '                    <FormItem>\n' +
    '                      <FormLabel className="text-gray-700 dark:text-gray-300">Password</FormLabel>\n' +
    '                      <FormControl>\n' +
    '                        <div className="relative">\n' +
    '                          <Input\n' +
    '                            placeholder="••••••••"\n' +
    '                            type={showPassword ? "text" : "password"}\n' +
    '                            autoComplete="current-password"\n' +
    '                            disabled={isLoading}\n' +
    '                            className="h-11 bg-gray-50 dark:bg-gray-900 border-gray-200 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-600 pr-10"\n' +
    '                            {...field}\n' +
    '                          />\n' +
    '                          <Button\n' +
    '                            type="button"\n' +
    '                            variant="ghost"\n' +
    '                            size="sm"\n' +
    '                            className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent text-gray-500 dark:text-gray-400"\n' +
    '                            onClick={() => setShowPassword(!showPassword)}\n' +
    '                            disabled={isLoading}\n' +
    '                          >\n' +
    '                            {showPassword ? (\n' +
    '                              <EyeOff className="h-4 w-4" />\n' +
    '                            ) : (\n' +
    '                              <Eye className="h-4 w-4" />\n' +
    '                            )}\n' +
    '                            <span className="sr-only">\n' +
    '                              {showPassword ? "Hide password" : "Show password"}\n' +
    '                            </span>\n' +
    '                          </Button>\n' +
    '                        </div>\n' +
    '                      </FormControl>\n' +
    '                      <FormMessage className="text-xs font-medium" />\n' +
    '                    </FormItem>\n' +
    '                  )}\n' +
    '                />\n' +
    '                <div className="flex items-center justify-between">\n' +
    '                  <FormField\n' +
    '                    control={form.control}\n' +
    '                    name="rememberMe"\n' +
    '                    render={({ field }) => (\n' +
    '                      <FormItem className="flex flex-row items-center space-x-2 space-y-0">\n' +
    '                        <FormControl>\n' +
    '                          <Checkbox\n' +
    '                            checked={field.value}\n' +
    '                            onCheckedChange={field.onChange}\n' +
    '                            disabled={isLoading}\n' +
    '                            className="text-blue-600 dark:text-blue-500 border-gray-300 dark:border-gray-600 rounded"\n' +
    '                          />\n' +
    '                        </FormControl>\n' +
    '                        <FormLabel className="text-sm font-medium leading-none cursor-pointer text-gray-600 dark:text-gray-400">\n' +
    '                          Remember me\n' +
    '                        </FormLabel>\n' +
    '                      </FormItem>\n' +
    '                    )}\n' +
    '                  />\n' +
    '                  <a \n' +
    '                    href="#" \n' +
    '                    className="text-sm font-medium text-blue-600 hover:text-blue-500 dark:text-blue-500 dark:hover:text-blue-400 hover:underline focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900"\n' +
    '                  >\n' +
    '                    Forgot password?\n' +
    '                  </a>\n' +
    '                </div>\n' +
    '                <Button \n' +
    '                  type="submit" \n' +
    '                  className="w-full h-11 bg-gradient-to-r from-blue-600 to-blue-500 hover:from-blue-700 hover:to-blue-600 dark:from-blue-600 dark:to-blue-500 dark:hover:from-blue-700 dark:hover:to-blue-600 text-white font-medium rounded-lg transition-all duration-200 shadow-md hover:shadow-lg"\n' +
    '                  disabled={isLoading}\n' +
    '                >\n' +
    '                  {isLoading ? (\n' +
    '                    <>\n' +
    '                      <Loader2 className="mr-2 h-4 w-4 animate-spin" />\n' +
    '                      Signing in...\n' +
    '                    </>\n' +
    '                  ) : (\n' +
    '                    "Sign in"\n' +
    '                  )}\n' +
    '                </Button>\n' +
    '              </form>\n' +
    '            </Form>\n' +
    '          </CardContent>\n' +
    '          <CardFooter className="flex flex-col space-y-4 px-6 py-5 bg-gray-50 dark:bg-gray-900/50 border-t border-gray-100 dark:border-gray-800">\n' +
    '            <div className="text-sm text-center text-gray-600 dark:text-gray-400">\n' +
    `              Don't have an account?{" "}\n` +
    '              <a \n' +
    '                href="#" \n' +
    '                className="font-medium text-blue-600 hover:text-blue-500 dark:text-blue-500 dark:hover:text-blue-400 hover:underline focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900"\n' +
    '              '... 365 more characters
}
生成效果

Compoder Cli

Compoder MCP Server

集成到 Cursor

集成到 Claude Code

总结