title: 如何贡献
description: 遵循以下准则以确保顺利合作
icon: "lucide:scroll-text"
---

感谢您有兴趣为 **Inspira UI** 项目做出贡献！您的贡献有助于让这个项目更好地服务于每个人。请花点时间阅读这些指南，以确保合作顺利进行。

## 目录

- [目录](#目录)
- [快速开始](#快速开始)
- [行为准则](#行为准则)
- [如何贡献](#如何贡献)
  - [错误报告](#错误报告)
  - [改进建议](#改进建议)
  - [添加新组件](#添加新组件)
    - [组件指南](#组件指南)
- [项目结构](#项目结构)
- [样式指南](#样式指南)
  - [代码标准](#代码标准)
  - [组件格式](#组件格式)
  - [提交信息](#提交信息)
- [文档指南](#文档指南)
  - [添加新组件的步骤](#添加新组件的步骤)
  - [单文件组件](#单文件组件)
  - [多文件组件](#多文件组件)
- [测试](#测试)
- [附加说明](#附加说明)
- [许可证](#许可证)

---

## 快速开始

- **分支代码库**: 在 GitHub 上创建项目的个人分支。
- **克隆你的分支**: 将你分支的代码库克隆到本地机器。
- **创建分支**: 为你的贡献创建一个新的分支 (`git checkout -b feature/YourFeatureName`).
- **安装依赖项**: 运行 `pnpm install` 安装所有必要的依赖项。

## 行为准则

通过参与此项目，您同意遵守[行为准则](./4.code-of-conduct.md)，该准则旨在营造开放和友好的环境。

## 如何贡献

### 错误报告

如果您发现错误，请提交[问题](https://github.com/unovue/inspira-ui/issues){:target="\_blank"} 并包含以下内容：

- 清晰且描述性的标题。
- 问题重现步骤。
- 预期结果和实际结果。
- 屏幕截图或代码片段（如果可以的话）。

### 改进建议

我们欢迎您对新功能或改进提出建议。请提交[问题](https://github.com/unovue/inspira-ui/issues){:target="\_blank"}并包含以下内容：

- 清晰且描述性的标题。
- 改进的详细描述。
- 任何相关的示例或模型。

### 添加新组件

我们非常感谢您为库添加新组件。请确保：

- 该组件总体上实用，并且符合项目目标。
- 该组件兼容 **Nuxt** 和 **Vue** 。
- 请遵循以下列出的编码和文档指南。
- 请为组件添加单元测试。
- 该组件总体上实用，并且符合项目目标。

#### 组件指南

1. **创建或更新 `index.ts`**  
   每个 `components/content/inspira/ui/<component-folder-name>/` 文件下都应包含一个 `index.ts` 以便导出所有 Vue 文件，例如：

   ```ts
   // index.ts
   export { default as Book } from "./Book.vue";
   export { default as BookHeader } from "./BookHeader.vue";
   export { default as BookTitle } from "./BookTitle.vue";
   export { default as BookDescription } from "./BookDescription.vue";
   ```

2. **注册依赖:**
   如果您的新组件依赖（或使用）其他 Inspira UI 组件，则必须更新 `~/scripts/crawl-content.ts` 中的 `COMPONENT_DEPENDENCIES` 映射以反映这些关系。这有助于库在用户通过 CLI 添加组件时了解应同时安装哪些组件。

3. **Nuxt 独有功能:**
   如果您的新组件或其示例使用了 Nuxt 独有功能（例如 `<ClientOnly>`），请在文档中提及。这可确保用户了解在 Nuxt 之外使用该组件时可能会遇到限制或需要额外步骤。

   这可确保用户可以通过 CLI 安装组件，并且所有依赖项都已正确声明。

4. **避免使用外部组件:**
   避免使用外部组件：创建组件时，避免使用不属于核心 Vue.js 生态系统的外部 UI 组件（如 `<UiButton>` 或类似组件）。

5. **显式导入:**
   即使你在开发过程中使用了 Nuxt 的自动导入功能，也请始终在组件代码中包含显式导入。这确保了与未启用自动导入的 Vue.js 用户的兼容性。例如：

   ```vue
   <script setup lang="ts">
   import { ref, onMounted } from "vue";
   import { useWindowSize } from "@vueuse/core";
   // Include all imports explicitly
   </script>
   ```

6. **图标使用:**
   如果您需要在演示或组件中使用图标，请使用内置的 `<Icon>` 组件，而不是将原始 SVG 粘贴到模板中。

## 项目结构

了解项目结构对于有效贡献至关重要：

- **组件目录**:
  - 主要组件应放置在 `components/content/inspira/ui/<component-folder-name>/` 。
    - 包含一个 `index.ts` 文件，用于导出该文件夹中的每个组件。
  - 示例组件应放置在 `components/content/inspira/examples/<component-folder-name>/` 。
- **文档**:
  - 文档文件位于 `content/2.components/<category>/` 目录中。
  - 添加组件后，请在此目录中编写其文档。

## 样式指南

### 代码标准

- **语言**: 所有组件均应使用 **Vue.js** 编写，并支持 TypeScript。
- **样式**: 尽可能使用 **Tailwind CSS** 类进行样式设置。
- **命名规范**: 组件名称和文件名均使用 `PascalCase（大驼峰命名，即单词首字母全大写）` 命名。

### 组件格式

您的 Vue 组件应遵循以下结构：

```vue
<!-- Template -->
<template>
  <!-- Your template code goes here -->
</template>

<!-- Script (if required) -->
<script lang="ts" setup>
// Your script code goes here
</script>

<!-- Styles (if required) -->
<style scoped>
/* Your styles go here */
</style>
```

**Props 类型和代码风格**

请参阅此 Vue.js 文档页面 -> [https://vuejs.org/api/sfc-script-setup#type-only-props-emit-declarations](https://vuejs.org/api/sfc-script-setup#type-only-props-emit-declarations)

```vue
<script lang="ts" setup>
// DON'T ⛔️
const props = defineProps({
  whatever: { type: String, required: true },
  optional: { type: String, default: "default" },
});

// DO ✅
interface Props {
  whatever: string;
  optional?: string;
}

const props = withDefaults(defineProps<Props>(), { optional: "default" });

// Or DO ✅ Props destructure (v3.5+)
interface Props {
  msg?: string;
  labels?: string[];
}

const { msg = "hello", labels = ["one", "two"] } = defineProps<Props>();
</script>
```

**常量、接口、类型和变量**

为了提高可复用性，您还可以在组件文件夹的根目录下添加一个 `index.ts` 文件，用于导出接口、常量和其他有用的代码元素。请注意，开发者会将组件代码复制粘贴到他们的项目中，因此根据他们的标准进行自定义应该非常容易。

变量必须使用 `CAPS_CAMEL_CASE` 格式，以便在代码中清晰识别。并添加 `前缀` 。请勿使用枚举；请使用 `{} as const`。😘

```typescript
// DON'T ⛔️
const Direction = { Top: 'top'} as const
const ComponentNameDirection = { Top: 'top'} as const

// DON'T ⛔️
enum COMPONENT_NAME_DIRECTION_WRONG = { Top = 'top'};

// DO ✅
import type { ObjectValues } from "@/lib/utils";
export const COMPONENT_NAME_DIRECTION = { Top: 'top', Bottom: 'bottom'} as const

//Types and Interfaces should use CamelCase to differentiate them from constants and variables.
export type ComponentNameDirection = ObjectValues<typeof COMPONENT_NAME_DIRECTION>;

interface {
   direction: ComponentNameDirection; //Enforce correct value : 'top' or 'bottom'
}
```

您可以查看 `PatternBackground` 组件文件 `components/content/inspira/ui/pattern-background` 以获取完整示例。

**注意:**

- 使用 `<script lang="ts" setup>` 来设置 TypeScript 和 Composition API。
- 保持样式的作用域以避免冲突。
- 确保与 **Nuxt** 和 **Vue** 。

### 提交信息

- 请使用[常规提交](https://www.conventionalcommits.org/)格式。
- 以类型（功能、修复、文档等）开头，后跟简短描述。
- 示例：`feat: add TextHoverEffect component`

## 文档指南

完善的文档对于用户理解和有效使用组件至关重要。为新组件添加文档时，请遵循以下指南。

### 添加新组件的步骤

1. **创建组件**
   - 将主组件放置在 `components/content/inspira/ui/<component-folder-name>/` 中。
   - 请遵循上面指定的[组件格式](#组件格式)。
   - 如果组件需要示例或演示，请将演示组件添加到 `components/content/inspira/examples/<component-folder-name>/` 中。

2. **编写文档**
   - 在 `content/2.components/<category>/` 目录下添加一个新的 Markdown 文件，用于编写组件的文档。
   - 根据组件是单文件还是多文件（见下文）使用合适的模板。
   - 如有需要，请添加实用工具类部分。
   - 如果组件是从其他 UI 库移植而来或从其他来源获取灵感，请注明来源和 **致谢** 。

3. **确保兼容性**
   - 在 **Nuxt** 和 **Vue** 环境中测试您的组件。

### 单文件组件

对于包含在单个 `.vue` 文件中的组件，请使用以下模板：

1. **前言**

   Begin with YAML front matter that includes the `title` and `description`:
   以包 `title` 和 `description` 的 YAML 前言开头：

   ```yaml
   ---
   title: Your Component Title
   description: A brief description of what your component does.
   ---
   ```

2. **预览部分**

   使用 `ComponentLoader` 显示组件的实时预览。 `id` 应设置为组件在 `components/content/inspira/examples/` 目录中的文件夹名称。如果没有该文件夹，则无需设置 `id` 。

   ```markdown
   ```vue
// Component source not found for YourComponentDemo.vue
```
   ```

3. **警告**

   如果您的组件有特殊要求或依赖项，请在安装说明之前添加警告部分：

   ```markdown
   ::alert{type="info"}
   **注意:** This component requires `package-name` as a dependency.
   ::

   ::alert{type="warning"}
   **注意:** This component uses the `nuxt-only` syntax with the `<ClientOnly>`. If you are not using Nuxt, you can simply remove it.
   ::
   ```

4. **安装**

   包含 CLI 和手动安装说明。如果需要额外设置（例如，依赖项、Tailwind 配置更新），请使用分步器列出所有必要步骤。

   ```markdown
   ## 通过 CLI 安装

   ```vue
<InstallationCli component-id="your-component-folder-name" />
```

   ## 手动安装

   复制并粘贴以下代码：

   ```vue
// Component source not found for YourComponent.vue
```
   ```

5. **API 文档**

   提供列出所有 props 的表格：

   ```markdown
   ## API

   | Prop 名称 | 类型      | 默认值 | 描述                           |
   | --------- | --------- | ------ | ------------------------------ |
   | `prop1`   | `string`  | `''`   | Description of prop1.          |
   | `prop2`   | `number`  | `0`    | Description of prop2.          |
   | `prop2`   | `?number` | `0`    | Description of prop2 optional. |
   ```

**示例:**

```markdown
---
title: Text Hover Effect
description: A text hover effect that animates and outlines gradient on hover, as seen on x.ai
---

```vue
<template>
  <ClientOnly>
    <div class="flex h-auto items-center justify-center max-lg:w-full min-md:flex-1">
      <TextHoverEffect
        class="w-[90%] min-lg:min-h-64"
        text="INSPIRA"
      />
    </div>
  </ClientOnly>
</template>

```

::alert{type="warning"}
This component uses the `nuxt-only` syntax with the `<ClientOnly>`. If you are not using Nuxt, you can simply remove it.
::

## 通过 CLI 安装

```vue
<InstallationCli component-id="text-hover-effect" />
```

## 手动安装

复制并粘贴以下代码：

```vue
<template>
  <svg
    ref="svgRef"
    width="100%"
    height="100%"
    viewBox="0 0 300 100"
    xmlns="http://www.w3.org/2000/svg"
    class="select-none"
    @mouseenter="handleMouseEnter"
    @mouseleave="handleMouseLeave"
    @mousemove="handleMouseMove"
    @touchstart="handleTouchStart"
    @touchmove="handleTouchMove"
    @touchend="handleTouchEnd"
  >
    <defs>
      <linearGradient
        id="textGradient"
        gradientUnits="userSpaceOnUse"
        cx="50%"
        cy="50%"
        r="25%"
      >
        <stop
          v-if="hovered"
          offset="0%"
          stop-color="var(--yellow-500)"
        />
        <stop
          v-if="hovered"
          offset="25%"
          stop-color="var(--red-500)"
        />
        <stop
          v-if="hovered"
          offset="50%"
          stop-color="var(--blue-500)"
        />
        <stop
          v-if="hovered"
          offset="75%"
          stop-color="var(--cyan-500)"
        />
        <stop
          v-if="hovered"
          offset="100%"
          stop-color="var(--violet-500)"
        />
      </linearGradient>

      <!-- Radial Gradient -->
      <radialGradient
        id="revealMask"
        gradientUnits="userSpaceOnUse"
        r="20%"
        :cx="maskPosition.cx"
        :cy="maskPosition.cy"
        :style="{
          transition: `cx ${transitionDuration}ms ease-out, cy ${transitionDuration}ms ease-out`,
        }"
      >
        <stop
          offset="0%"
          stop-color="white"
        />
        <stop
          offset="100%"
          stop-color="black"
        />
      </radialGradient>

      <mask id="textMask">
        <rect
          x="0"
          y="0"
          width="100%"
          height="100%"
          fill="url(#revealMask)"
        />
      </mask>
    </defs>

    <text
      x="50%"
      y="50%"
      text-anchor="middle"
      dominant-baseline="middle"
      :stroke-width="strokeWidth"
      :style="{ opacity: hovered ? opacity : 0 }"
      class="fill-transparent stroke-neutral-200 font-[helvetica] text-7xl font-bold dark:stroke-neutral-800"
    >
      {{ text }}
    </text>

    <!-- Animated Text Stroke -->
    <text
      x="50%"
      y="50%"
      text-anchor="middle"
      dominant-baseline="middle"
      :stroke-width="strokeWidth"
      :style="strokeStyle"
      class="fill-transparent stroke-neutral-200 font-[helvetica] text-7xl font-bold dark:stroke-neutral-800"
    >
      {{ text }}
    </text>

    <text
      x="50%"
      y="50%"
      text-anchor="middle"
      dominant-baseline="middle"
      stroke="url(#textGradient)"
      :stroke-width="strokeWidth"
      mask="url(#textMask)"
      class="fill-transparent font-[helvetica] text-7xl font-bold"
    >
      {{ text }}
    </text>
  </svg>
</template>

<script setup lang="ts">
import { ref, reactive, computed } from "vue";

interface Props {
  strokeWidth?: number;
  text: string;
  duration?: number;
  opacity?: number;
}

const props = withDefaults(defineProps<Props>(), {
  strokeWidth: 0.75,
  duration: 200,
  opacity: 0.75,
});

const svgRef = ref<SVGSVGElement | null>(null);
const cursor = reactive({ x: 0, y: 0 });
const hovered = ref(false);

// Set transition duration for smoother animation
const transitionDuration = props.duration ? props.duration * 1000 : 200;

// Reactive gradient position
const maskPosition = computed(() => {
  if (svgRef.value) {
    const svgRect = svgRef.value.getBoundingClientRect();
    const cxPercentage = ((cursor.x - svgRect.left) / svgRect.width) * 100;
    const cyPercentage = ((cursor.y - svgRect.top) / svgRect.height) * 100;
    return { cx: `${cxPercentage}%`, cy: `${cyPercentage}%` };
  }
  return { cx: "50%", cy: "50%" }; // Default position
});

// Reactive style for stroke animation
const strokeStyle = computed(() => ({
  strokeDashoffset: hovered.value ? "0" : "1000",
  strokeDasharray: "1000",
  transition: "stroke-dashoffset 4s ease-in-out, stroke-dasharray 4s ease-in-out",
}));

function handleMouseEnter() {
  hovered.value = true;
}

function handleMouseLeave() {
  hovered.value = false;
}

function handleMouseMove(e: MouseEvent) {
  cursor.x = e.clientX;
  cursor.y = e.clientY;
}

// Touch support
function handleTouchStart(e: TouchEvent) {
  hovered.value = true;
  handleTouchMove(e); // Update the position on touch start
}

function handleTouchMove(e: TouchEvent) {
  const touch = e.touches[0];
  cursor.x = touch.clientX;
  cursor.y = touch.clientY;
}

function handleTouchEnd() {
  hovered.value = false;
}
</script>

<style scoped>
.select-none {
  user-select: none;
}
</style>

```

## API

| Prop 名称     | 类型     | 默认值   | 描述                                                      |
| ------------- | -------- | -------- | --------------------------------------------------------- |
| `text`        | `string` | Required | The text to be displayed with the hover effect.           |
| `duration`    | `number` | `200`    | The duration of the mask transition animation in seconds. |
| `strokeWidth` | `number` | `0.75`   | The width of the text stroke.                             |
| `opacity`     | `number` | `null`   | The opacity of the text.                                  |
```

在这个例子中，`ComponentLoader` 、 `CodeViewer` 和 `InstallationCli` 中使用的 `id` 都是 `text-hover-effect` ，它与存储组件及其演示的文件夹名称匹配。

### 多文件组件

对于由多个文件组成的组件，例如主组件和多个子组件或变体，请使用以下模板：

1. **前言**

   从 YAML 前言开始:

   ```yaml
   ---
   title: Your Components Group Title
   description: A brief description of what this group of components does.
   ---
   ```

2. **预览部分**

   为每个示例或变体添加多个 `ComponentLoader` 部分。 `id` 应设置为 `components/content/inspira/examples/` 中组件的文件夹名称。

   ```markdown
   ```vue
// Component source not found for ComponentVariantDemo.vue
```
   ```

3. **警告**

   如果您的组件有特殊要求或依赖关系，请在安装说明之前添加警告部分：

   ```markdown
   ::alert{type="info"}
   **注意:** This component requires `package-name` as a dependency.
   ::

   ::alert{type="warning"}
   **注意:** This component uses the `nuxt-only` syntax with the `<ClientOnly>`. If you are not using Nuxt, you can simply remove it.
   ::
   ```

4. **安装**

   包含 CLI 和手动安装说明。如果需要额外设置（例如，依赖项、Tailwind 配置更新），请使用分步器列出所有所需步骤。

   ```markdown
   ## 通过 CLI 安装

   ```vue
<InstallationCli component-id="your-component-folder-name" />
```

   ## 手动安装

   在同一个文件中，复制并粘贴以下代码：

   ::code-group

   :CodeViewerTab{label="YourComponent.vue" language="vue" componentName="YourComponent" type="ui" id="your-component-folder-name"}
   :CodeViewerTab{filename="YourComponent2.vue" language="vue" componentName="YourComponent2" type="ui" id="your-component-folder-name"}

   ::
   ```

5. **API 文档**

   提供涵盖所有组件的全面 API 文档：

   ```markdown
   ## API

   | Prop 名称 | 类型     | 默认值 | 描述                                    |
   | --------- | -------- | ------ | --------------------------------------- |
   | `prop1`   | `string` | `''`   | Description applicable to all variants. |
   ```

**示例:**

```markdown
---
title: Pattern Background
description: Simple animated pattern background to make your sections stand out.
---

Grid background with dot
```vue
<template>
  <PatternBackground
    :animate="true"
    :direction="PATTERN_BACKGROUND_DIRECTION.TopRight"
    :variant="PATTERN_BACKGROUND_VARIANT.Dot"
    class="flex h-[36rem] w-full items-center justify-center"
    :speed="PATTERN_BACKGROUND_SPEED.Slow"
  >
    <p
      class="relative z-20 bg-gradient-to-b from-neutral-200 to-neutral-500 bg-clip-text py-8 text-4xl font-bold text-transparent sm:text-5xl"
    >
      Dot Background
    </p>
  </PatternBackground>
</template>

<script setup lang="ts">
import {
  PATTERN_BACKGROUND_DIRECTION,
  PATTERN_BACKGROUND_SPEED,
  PATTERN_BACKGROUND_VARIANT,
} from "../../ui/pattern-background";
</script>

```

## 通过 CLI 安装

```vue
<InstallationCli component-id="pattern-background" />
```

## 手动安装

在同一个文件中，复制并粘贴以下代码：

::code-group

:CodeViewerTab{label="PatternBackground.vue" language="vue" componentName="PatternBackground" type="ui" id="pattern-background"}
:CodeViewerTab{filename="index.ts" language="typescript" componentName="index" type="ui" id="pattern-background" extension="ts"}

::

## 示例

Grid background with big dot and ellipse on top
```vue
<template>
  <PatternBackground
    :animate="true"
    :direction="PATTERN_BACKGROUND_DIRECTION.Bottom"
    :variant="PATTERN_BACKGROUND_VARIANT.BigDot"
    class="flex h-[36rem] w-full items-center justify-center"
    :speed="PATTERN_BACKGROUND_SPEED.Slow"
    :mask="PATTERN_BACKGROUND_MASK.EllipseTop"
  >
    <p
      class="relative z-20 bg-gradient-to-b from-neutral-200 to-neutral-500 bg-clip-text py-8 text-4xl font-bold text-transparent sm:text-5xl"
    >
      Big Dot Background
    </p>
  </PatternBackground>
</template>

<script setup lang="ts">
import {
  PATTERN_BACKGROUND_DIRECTION,
  PATTERN_BACKGROUND_MASK,
  PATTERN_BACKGROUND_SPEED,
  PATTERN_BACKGROUND_VARIANT,
} from "../../ui/pattern-background";
</script>

```

Grid background without animation
```vue
<template>
  <PatternBackground
    :direction="PATTERN_BACKGROUND_DIRECTION.TopRight"
    :variant="PATTERN_BACKGROUND_VARIANT.Grid"
    class="flex h-[36rem] w-full items-center justify-center"
  >
    <p
      class="relative z-20 bg-gradient-to-b from-neutral-200 to-neutral-500 bg-clip-text py-8 text-4xl font-bold text-transparent sm:text-5xl"
    >
      Grid Background
    </p>
  </PatternBackground>
</template>

<script setup lang="ts">
import {
  PATTERN_BACKGROUND_DIRECTION,
  PATTERN_BACKGROUND_VARIANT,
} from "../../ui/pattern-background";
</script>

```

Small grid background with animation
```vue
<template>
  <PatternBackground
    :animate="true"
    :direction="PATTERN_BACKGROUND_DIRECTION.Right"
    :variant="PATTERN_BACKGROUND_VARIANT.Grid"
    class="flex h-[36rem] w-full items-center justify-center"
    size="xs"
    :speed="PATTERN_BACKGROUND_SPEED.Slow"
  >
    <p
      class="relative z-20 bg-gradient-to-b from-neutral-200 to-neutral-500 bg-clip-text py-8 text-4xl font-bold text-transparent sm:text-5xl"
    >
      Small Grid Background
    </p>
  </PatternBackground>
</template>

<script setup lang="ts">
import {
  PATTERN_BACKGROUND_DIRECTION,
  PATTERN_BACKGROUND_SPEED,
  PATTERN_BACKGROUND_VARIANT,
} from "../../ui/pattern-background";
</script>

```

## API

| Prop 名称   | 类型                                                                                                   | 默认值    | 描述                                                                                                                                                           |
| ----------- | ------------------------------------------------------------------------------------------------------ | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `animate`   | `boolean`                                                                                              | `false`   | Set `true` if you want to animate the background.                                                                                                              |
| `direction` | `top` \| `bottom` \| `left` \| `right` \| `top-left` \| `top-right` \| `bottom-left` \| `bottom-right` | `top`     | Direction of the animation movement. You can use the const `PATTERN_BACKGROUND_DIRECTION.`                                                                     |
| `direction` | `grid` \| `dot`                                                                                        | `grid`    | 类型 of pattern. You can use the const `PATTERN_BACKGROUND_VARIANT.`                                                                                           |
| `size`      | `xs` \| `sm` \| `md` \| `lg`                                                                           | `md`      | Size of the background pattern.                                                                                                                                |
| `mask`      | `ellipse` \| `ellipse-top`                                                                             | `ellipse` | Add a mask over the background pattern. You can use the const `PATTERN_BACKGROUND_MASK.`                                                                       |
| `speed`     | `number`                                                                                               | `10000`   | Duration of the animation in `ms`, the bigger it is, the slower the animation. (`20000` slower than `5000`). You can use the const `PATTERN_BACKGROUND_SPEED.` |

### 自定义变量、值和常量

您可以直接在 `index.ts` 文件中自定义您的需求。请参阅下方代码。

## 感谢

- 灵感来自 [Magic UI's Dot Pattern](https://magicui.design/docs/components/dot-pattern) 组件。
- 灵感来自 [Magic UI's Grid Pattern](https://magicui.design/docs/components/grid-pattern) 组件。
- 灵感来自 [Magic UI's Animated Grid Pattern](https://magicui.design/docs/components/animated-grid-pattern) 组件。
- 感谢 [Nathan De Pachtere](https://nathandepachtere.com/) ，移植了该组件。
```

## 测试

- **单元测试**: 如果可以，请为组件编写单元测试。
- **跨环境测试**: 确保您的组件在 **Nuxt** 和 **Vue** 环境中都能正常工作
- **可视化测试**: 直观地检查组件，确保其渲染正确。
- **CLI 安装测试**: 使用 `pnpm build:registry` 更新注册表后，通过引用本地注册表 URL 在单独的项目中测试组件安装。例如：

  ```sh
  npx shadcn-vue@latest add "https://localhost:3000/r/<component-name>"
  ```

## 附加说明

- **组件命名**: 使用 `PascalCase` 作为组件文件名和名称。
- **IDs**: 在 `CodeViewer` 、`CodeViewerTab` 和 `ComponentLoader` 中， `id` 参数应设置为组件所在文件夹的名称，即 `components/content/inspira/ui/<component-folder-name>/` 和 `components/content/inspira/examples/<component-folder-name>/` 。这有助于正确链接文档中的代码和示例。
- **演示组件**: 对于每个组件，创建一个在 `ComponentLoader` 中用于预览的相应演示组件，并将其放置在 `components/content/inspira/examples/<component-folder-name>/` 中。
- **本地化**: 如果您的组件支持多种语言，请在文档中包含详细信息。

## 许可证

通过贡献，您同意您的贡献将根据 [MIT 许可证](https://github.com/unovue/inspira-ui/blob/main/LICENSE){:target="\_blank"}进行许可。