contentstack
nuxt-contentstack

用于 Nuxt 的 Contentstack 集成

Nuxt Contentstack

Nuxt Contentstack

npm versionnpm downloadsLicenseNuxt

用于 Nuxt 的 Contentstack 集成。

注意:这是一个由 @timbenniks 维护的 OSS 项目,**不是**由 Contentstack 团队官方维护的包。支持请求可以通过 Github issues 和直接渠道联系 @timbenniks。

功能

  • ⚡️ 轻松设置
  • ⚡️ Contentstack 的完整 Vue 可组合项集合
  • ⚡️ 通过高级筛选查询条目和资产
  • ⚡️ 高级筛选和分页
  • ⚡️ 带有响应式 URL 的图像转换
  • ⚡️ @nuxt/image 集成,自动优化
  • ⚡️ 实时预览和可视化构建器
  • ⚡️ 个性化支持
  • ⚡️ 带有完整类型安全的 TypeScript 支持
  • ⚡️ 暴露的 SDK:TS Delivery SDK、Live Preview Utils SDK、Personalize SDK

快速设置

通过一个命令将模块安装到您的 Nuxt 应用中

npx nuxi module add nuxt-contentstack

或者:添加到 nuxt.config.ts

modules: ['nuxt-contentstack'],

'nuxt-contentstack': {
  // Required core settings
  apiKey: 'your_contentstack_api_key',
  deliveryToken: 'your_delivery_token',
  environment: 'your_environment',

  // Optional settings with smart defaults
  region: 'eu',
  branch: 'main',
  locale: 'en-us',

  // Live Preview
  livePreview: {
    enable: true,
    previewToken: 'your_preview_token', // no need for preview token if you are not using live preview
    editableTags: true,
    editButton: true
  },

  // Personalization
  personalization: {
    enable: true,
    projectUid: 'your_project_uid'
  },

  debug: true
},

选项

核心设置(必填)

  • apiKey - 您的 Contentstack 堆栈 API 密钥(以“blt”开头)
  • deliveryToken - 您的 Contentstack 交付令牌(以“cs”开头)
  • environment - 目标环境(“preview”|“production”)

核心设置(可选)

  • region - Contentstack 区域:“us”|“eu”|“au”|“azure-na”|“azure-eu”|“gcp-na”|“gcp-eu”(默认值:“us”)
  • branch - 内容分支(默认值:“main”)
  • locale - 默认语言环境(默认值:“en-us”)

实时预览设置

配置 livePreview 对象,包含:

  • enable - 启用实时预览模式
  • previewToken - Contentstack 的预览令牌(以“cs”开头,如果启用则为必填)
  • editableTags - 添加可编辑标签以进行可视化构建(也控制自动 CSLP 属性清理)
  • editButton - 启用编辑按钮(布尔值或详细配置对象)
  • mode - 实时预览模式:“builder”|“preview”(默认值:“builder”)
  • ssr - 启用 SSR 模式(实验性,默认值:false)

个性化设置

配置 personalization 对象,包含:

  • enable - 启用个性化功能
  • projectUid - 您的个性化项目 UID(在 Contentstack UI 中找到)

通用设置

  • debug - 启用调试日志和配置转储

个性化示例

// get Personalize SDK
const { Personalize } = useNuxtApp().$contentstack;

// set attribute
await Personalize.set({ age: 20 });

// trigger impression
// experienceShortId to be found on the experiences list page in contentstack
experienceShortId = 0;
await Personalize.triggerImpression(experienceShortId);

// trigger conversion event
// 'eventKey' can be found when creatign an event in Contentstack Personalize
await Personalize.triggerEvent("eventKey");

提供

此模块提供一个 $contentstack 对象,包含:

  • stack:来自 Delivery SDK 的 Stack 对象。使用它查询所有内容。
  • ContentstackLivePreview:Live Preview Utils SDK 的实例。
  • livePreviewEnabled:是否启用了实时预览?
  • editableTags:我们是否需要用于可视化构建的可编辑标签?
  • Personalize:Personalize SDK 的实例。
  • variantAlias:要传递给 Delivery SDK 的变体清单。
const {
  editableTags,
  stack,
  livePreviewEnabled,
  ContentstackLivePreview,
  Personalize,
  variantAlias,
  VB_EmptyBlockParentClass,
} = useNuxtApp().$contentstack;

组件

此模块为常见的 Contentstack 用例提供了 Vue 组件。

ContentstackModularBlocks

一个灵活、通用的组件,用于将 Contentstack 模块块渲染为 Vue 组件。非常适合动态页面布局、组件库和内容驱动的 UI。

功能

  • 自动组件映射 - 自动将 Contentstack 块类型映射到 Vue 组件
  • 自动获取能力 - 可以自动获取条目数据并提取块(新功能)
  • 灵活的数据结构 - 支持各种 Contentstack 模块块格式
  • 实时预览就绪 - 全面支持 Contentstack 实时预览,带有 data-cslp 属性
  • 可视化构建器支持 - 包含用于可视化构建的空状态类
  • TypeScript 支持 - 带有泛型的全面类型定义
  • SSR 兼容 - 在服务器上完美渲染并无缝水合
  • 可定制样式 - 可配置的 CSS 类和容器属性
  • 错误处理 - 未映射组件的优雅回退
  • 插槽支持 - 通过插槽实现自定义加载、错误和空状态内容

使用模式

该组件支持两种使用模式,以实现最大的灵活性

模式 1:自动获取条目 + 渲染块(新功能)

非常适合简单的页面渲染 - 只需提供条目详细信息,让组件处理所有事情

<script setup>
import Hero from "./components/Hero.vue";
import Grid from "./components/Grid.vue";
import TextBlock from "./components/TextBlock.vue";

// Map Contentstack block types to Vue components
const componentMapping = {
  hero: Hero,
  grid: Grid,
  text_block: TextBlock,
};
</script>

<template>
  <!-- Component fetches entry and renders blocks automatically -->
  <ContentstackModularBlocks
    content-type-uid="page"
    :url="$route.path"
    blocks-field-path="components"
    :reference-field-path="['blocks.block.image']"
    :json-rte-path="['rich_text', 'blocks.block.copy']"
    locale="en-us"
    :component-map="componentMapping"
  >
    <!-- Custom loading state -->
    <template #loading>
      <div class="loading-spinner">Loading page content...</div>
    </template>

    <!-- Custom error state -->
    <template #error>
      <div class="error-message">Failed to load content</div>
    </template>
  </ContentstackModularBlocks>
</template>

模式 2:传统模式,带预取块

当您需要对数据获取有更多控制时使用

<script setup>
import Hero from "./components/Hero.vue";
import Grid from "./components/Grid.vue";
import TextBlock from "./components/TextBlock.vue";

// Map Contentstack block types to Vue components
const componentMapping = {
  hero: Hero,
  grid: Grid,
  text_block: TextBlock,
};

// Fetch your page data manually
const { data: page } = await useGetEntryByUrl({
  contentTypeUid: "page",
  url: useRoute().path,
});
</script>

<template>
  <!-- Pass pre-fetched blocks -->
  <ContentstackModularBlocks
    :blocks="page.components"
    :component-map="componentMapping"
  />
</template>

高级用法

<script setup>
import Hero from "./blocks/Hero.vue";
import Grid from "./blocks/Grid.vue";
import DefaultBlock from "./blocks/DefaultBlock.vue";

const componentMapping = {
  hero: Hero,
  grid: Grid,
  text_section: TextSection,
  image_gallery: ImageGallery,
};
</script>

<template>
  <ContentstackModularBlocks
    :blocks="page.modular_blocks"
    :component-map="componentMapping"
    :fallback-component="DefaultBlock"
    :auto-extract-block-name="true"
    :show-empty-state="true"
    container-class="page-blocks"
    empty-block-class="visual-builder__empty-block-parent"
    empty-state-message="No content blocks available"
    key-field="_metadata.uid"
    block-name-prefix="block_"
    :container-props="{ 'data-page-id': page.uid }"
  >
    <!-- Custom empty state -->
    <template #empty>
      <div class="custom-empty-state">
        <h3>No content blocks found</h3>
        <p>Please add some content in Contentstack</p>
      </div>
    </template>
  </ContentstackModularBlocks>
</template>

属性

核心属性

属性类型默认描述
blocksContentstackBlock[][]Contentstack 模块块数组
componentMapComponentMapping{}将块类型映射到 Vue 组件的对象
fallbackComponentComponent | stringContentstackFallbackBlock未映射块类型的回退组件

自动获取属性(新功能)

属性类型默认描述
contentTypeUidstring未定义用于获取条目的内容类型 UID
urlstring未定义通过 URL 获取条目
referenceFieldPathstring[][]要包含的引用字段路径
jsonRtePathstring[][]JSON RTE 字段路径
localestring'en-us'条目的语言环境
replaceHtmlCslpbooleanfalse替换 HTML CSLP 标签
blocksFieldPathstring'components'从中提取模块块的字段路径
seoMetaSeoMetaInput未定义SEO 元数据对象(直接传递给 useSeoMeta)
autoSeoMetaboolean | Record<string, string>false从条目数据自动生成 SEO

样式属性

属性类型默认描述
containerClassstring'contentstack-modular-blocks'容器的 CSS 类
emptyBlockClassstring'visual-builder__empty-block-parent'空块的 CSS 类(可视化构建器)
containerPropsRecord<string, any>{}绑定到容器的其他属性
showEmptyStatebooleantrue当没有块时显示空状态
emptyStateClassstring'contentstack-empty-state'空状态的 CSS 类
emptyStateMessagestring'没有内容块可用'空状态中显示的消息

高级属性

属性类型默认描述
keyFieldstring'_metadata.uid'块的自定义键字段
autoExtractBlockNamebooleantrue从对象键中自动提取块名称
blockNamePrefixstring''要从块名称中删除的前缀

SEO 元数据支持(新功能)

该组件在使用自动获取模式时可以自动设置 SEO 元数据,使用 Nuxt 的原生 useSeoMeta 类型和功能。

简单用法

<template>
  <!-- Pass SEO metadata directly using useSeoMeta format -->
  <ContentstackModularBlocks
    content-type-uid="page"
    :url="$route.path"
    :seo-meta="{
      title: 'My Page Title',
      description: 'My page description',
      ogTitle: 'My Social Title',
      ogDescription: 'My social description',
      ogImage: 'https://example.com/image.jpg',
      twitterCard: 'summary_large_image',
    }"
    :component-map="componentMapping"
  />
</template>

来自条目数据的动态 SEO

<script setup>
// Get entry data first, then use it for SEO
const { data: page } = await useGetEntryByUrl({
  contentTypeUid: "page",
  url: useRoute().path,
});

// Create SEO object from entry data
const seoMeta = computed(() => ({
  title: page.value?.seo_title || page.value?.title,
  description: page.value?.seo_description || page.value?.description,
  ogTitle: page.value?.social_title || page.value?.title,
  ogDescription: page.value?.social_description || page.value?.description,
  ogImage: page.value?.featured_image?.url,
  canonical: `https://example.com${page.value?.url}`,
  robots: page.value?.no_index ? "noindex,nofollow" : "index,follow",
}));
</script>

<template>
  <!-- Use computed SEO metadata -->
  <ContentstackModularBlocks
    :blocks="page?.components"
    :seo-meta="seoMeta"
    :component-map="componentMapping"
  />
</template>

从条目数据自动生成 SEO

当使用自动获取模式时,您可以从获取的条目数据中自动生成 SEO 元数据

<template>
  <!-- Auto-generate SEO using default field mapping -->
  <ContentstackModularBlocks
    content-type-uid="page"
    :url="$route.path"
    :auto-seo-meta="true"
    :component-map="componentMapping"
  />
</template>

自定义字段映射

<template>
  <!-- Custom field mapping for SEO generation -->
  <ContentstackModularBlocks
    content-type-uid="page"
    :url="$route.path"
    :auto-seo-meta="{
      title: 'page_title|title',
      description: 'meta_description|description',
      ogTitle: 'social_title|page_title|title',
      ogImage: 'featured_image.url',
      canonical: 'canonical_url',
      twitterCard: 'summary_large_image',
    }"
    :component-map="componentMapping"
  />
</template>

默认自动 SEO 字段映射

autoSeoMeta: true 时,这些字段会自动映射

SEO 元标签条目字段(回退顺序)
titleseo_titletitlename
描述seo_descriptiondescriptionsummary
ogTitleseo_titletitlename
ogDescriptionseo_descriptiondescriptionsummary
ogImagefeatured_image.urlog_image.urlimage.url

结合手动和自动 SEO

<template>
  <!-- Auto-generate base SEO, override specific fields -->
  <ContentstackModularBlocks
    content-type-uid="page"
    :url="$route.path"
    :auto-seo-meta="true"
    :seo-meta="{
      canonical: 'https://example.com' + $route.path,
      robots: 'index,follow',
    }"
    :component-map="componentMapping"
  />
</template>

支持所有 useSeoMeta 选项

由于我们将 seoMeta prop 直接传递给 Nuxt 的 useSeoMeta,因此您可以使用 100 多个受支持的元标签中的任何一个

优点

  • XSS 安全:使用 Nuxt 内置的 useSeoMeta 进行安全的元标签处理
  • TypeScript 支持:具有 100 多种元标签类型的完整类型安全
  • 自动生成:自动从获取的条目数据中提取 SEO
  • 灵活映射:支持回退的自定义字段映射
  • 优先级系统:手动 seoMeta 覆盖自动生成的值
  • SSR 优先:SEO 元数据在服务器端渲染期间设置,以实现最佳 SEO
  • 搜索引擎就绪:爬虫访问您的页面时可用的元标签
  • 灵活:可选择自动检测、自定义映射或基于函数逻辑

数据结构支持

该组件支持两种常见的 Contentstack 模块块结构

自动提取(默认)

{
  "components": [
    {
      "hero": {
        "title": "Welcome",
        "subtitle": "To our site"
      },
      "_metadata": { "uid": "hero_123" }
    },
    {
      "grid": {
        "columns": 3,
        "items": [...]
      },
      "_metadata": { "uid": "grid_456" }
    }
  ]
}

基于内容类型

{
  "modular_blocks": [
    {
      "_content_type_uid": "hero_block",
      "title": "Welcome",
      "subtitle": "To our site",
      "_metadata": { "uid": "hero_123" }
    },
    {
      "_content_type_uid": "grid_block",
      "columns": 3,
      "items": [...],
      "_metadata": { "uid": "grid_456" }
    }
  ]
}

组件属性

每个渲染的组件都会接收

// Original block props
{
  title: "Welcome",
  subtitle: "To our site",
  // ... other block fields

  // Additional meta props
  blockType: "hero",
  blockMetadata: { uid: "hero_123", ... }
}

实时预览集成

组件自动添加实时预览属性

<section class="contentstack-modular-blocks">
  <component
    :is="Hero"
    :title="Welcome"
    data-block-type="hero"
    data-block-index="0"
    data-cslp="hero.title"
  />
</section>

插槽

该组件提供了几个插槽,用于自定义不同状态

插槽描述可用时机
loading自定义加载状态内容自动获取已启用且数据正在加载
error自定义错误状态内容自动获取失败或遇到错误
empty自定义空状态内容没有可用块可渲染
<template>
  <ContentstackModularBlocks
    content-type-uid="page"
    :url="$route.path"
    :component-map="componentMapping"
  >
    <!-- Custom loading spinner -->
    <template #loading>
      <div class="flex items-center justify-center py-12">
        <div
          class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"
        ></div>
        <span class="ml-3">Loading page content...</span>
      </div>
    </template>

    <!-- Custom error message -->
    <template #error>
      <div class="bg-red-50 border border-red-200 rounded-lg p-6 text-center">
        <h3 class="text-red-800 font-semibold">Content Unavailable</h3>
        <p class="text-red-600 mt-2">
          Unable to load page content. Please try again later.
        </p>
      </div>
    </template>

    <!-- Custom empty state -->
    <template #empty>
      <div class="text-center py-12 text-gray-500">
        <h3 class="text-lg font-medium">No Content Available</h3>
        <p class="mt-2">This page doesn't have any content blocks yet.</p>
      </div>
    </template>
  </ContentstackModularBlocks>
</template>

错误处理

  • 缺失组件:回退到 fallbackComponent
  • 空块:显示可配置的空状态(可通过 #empty 插槽自定义)
  • 无效数据:优雅地处理格式错误的块数据
  • 缺失键:使用基于索引的键作为回退
  • 自动获取错误:显示错误状态(可通过 #error 插槽自定义)
  • 加载状态:在自动获取期间显示加载状态(可通过 #loading 插槽自定义)

ContentstackFallbackBlock

该模块包含一个内置的回退组件,为未映射的块类型提供了开发者友好的显示。该组件自动:

  • 显示块标题(来自 titlenameheadingblockType 字段)
  • 在样式徽章中显示块类型
  • 在可展开的详细信息部分将所有属性渲染为格式化的 JSON
  • 提供有关如何正确映射组件的有用指导
  • 支持暗模式以提供更好的开发者体验

功能

  • 🎨 风格化界面,具有清晰的视觉层次结构
  • 📱 响应式设计,适用于所有屏幕尺寸
  • 🌙 暗模式支持,通过 prefers-color-scheme
  • 🔍 可折叠 JSON,避免 UI 混乱
  • 🛠️ 开发者提示,显示如何修复未映射的组件

示例输出

┌─────────────────────────────────────┐
│ Welcome Hero                Type: hero │
├─────────────────────────────────────┤
│ ▶ View Props                        │
│   {                                 │
│     "title": "Welcome Hero",        │
│     "subtitle": "Get started now",  │
│     "cta_text": "Learn More"       │
│   }                                 │
├─────────────────────────────────────┤
│ This is a fallback component.       │
│ Map "hero" to a proper Vue component│
└─────────────────────────────────────┘

您可以通过提供自己的 fallbackComponent 来覆盖此设置

<ContentstackModularBlocks
  :blocks="page.components"
  :component-map="componentMapping"
  :fallback-component="MyCustomFallback"
/>

可组合项

此模块提供了几个可组合项,用于处理 Contentstack 内容。所有可组合项都支持实时预览、个性化并使用 Nuxt 的缓存系统。

useGetEntryByUrl

使用 URL 字段查询任何条目。监听实时编辑更改并支持个性化。

const { data: page } = await useGetEntryByUrl<Page>({
  contentTypeUid: "page",
  url: "/about",
  referenceFieldPath: ["reference.fields"],
  jsonRtePath: ["rich_text_field"],
  locale: "en-us",
  replaceHtmlCslp: true,
});

useGetEntry

通过其 UID 获取单个条目。

const { data: article } = await useGetEntry<Article>({
  contentTypeUid: "article",
  entryUid: "your_entry_uid",
  referenceFieldPath: ["author", "category"],
  jsonRtePath: ["content"],
  locale: "en-us",
});

useGetEntries

通过筛选、分页和排序获取多个条目。

const { data: articles } = await useGetEntries<Article>({
  contentTypeUid: "article",
  referenceFieldPath: ["author"],
  locale: "en-us",
  limit: 10,
  skip: 0,
  orderBy: "created_at",
  includeCount: true,
  where: {
    status: "published",
    published_at: { $gte: "2024-01-01" },
    tags: { $exists: true },
  },
});

// Access results
console.log(articles.value?.entries); // Article[]
console.log(articles.value?.count); // Total count if includeCount: true

useGetAsset

通过其 UID 获取单个资产。

const { data: image } = await useGetAsset<Asset>({
  assetUid: "your_asset_uid",
  locale: "en-us",
});

useGetAssets

通过筛选和分页获取多个资产。

const { data: images } = await useGetAssets<Asset>({
  locale: "en-us",
  limit: 20,
  orderBy: "created_at",
  where: {
    content_type: "image/jpeg",
    // Note: Asset filtering is limited; most filters are applied client-side
  },
});

查询运算符

条目可组合项(useGetEntriesuseGetEntryuseGetEntryByUrl)在 where 参数中支持高级查询运算符

注意:资产筛选的服务器端支持有限。useGetAssets 可组合项在获取后将大多数筛选器应用于客户端。

where: {
  // Exact match
  status: "published",

  // Array contains
  tags: ["tech", "news"],

  // Comparison operators
  view_count: { $gt: 1000 },
  created_at: { $gte: "2024-01-01", $lt: "2024-12-31" },

  // Existence checks
  featured_image: { $exists: true },

  // Pattern matching
  title: { $regex: "nuxt.*contentstack" },

  // Not equal
  author: { $ne: "guest" }
}

@nuxt/image 集成

此模块包含一个自定义的 @nuxt/image 提供程序,用于与 Contentstack 的图像交付 API 无缝集成。

设置

  1. 安装 @nuxt/image
npm install @nuxt/image
  1. 将这两个模块添加到您的 nuxt.config.ts
export default defineNuxtConfig({
  modules: ["nuxt-contentstack", "@nuxt/image"],

  // Configure @nuxt/image to use Contentstack provider
  image: {
    providers: {
      contentstack: {
        name: "contentstack",
        provider:
          "node_modules/nuxt-contentstack/dist/runtime/providers/contentstack",
        options: {},
      },
    },
    // Optional: Set Contentstack as default provider
    provider: "contentstack",
  },
});

使用

配置完成后,您可以直接使用 Contentstack 资产 URL 的 <NuxtImg><NuxtPicture> 组件 - 无需将资产 UID 或版本 UID 作为修饰符传递

<template>
  <!-- Basic usage with Contentstack assets -->
  <NuxtImg
    :src="page.image.url"
    :alt="page.image.title"
    width="800"
    height="400"
    :modifiers="{
      auto: 'webp,compress',
      quality: 90,
    }"
    provider="contentstack"
  />

  <!-- Responsive image with automatic optimization -->
  <NuxtImg
    :src="hero.image.url"
    :alt="hero.image.title"
    sizes="100vw sm:50vw lg:33vw"
    densities="1x 2x"
    :modifiers="{
      auto: 'webp,compress',
      quality: 90,
    }"
    provider="contentstack"
  />

  <!-- Advanced transformations -->
  <NuxtImg
    :src="gallery.image.url"
    width="600"
    height="400"
    fit="cover"
    :modifiers="{
      blur: 5,
      brightness: 110,
      contrast: 120,
      saturation: 130,
    }"
    provider="contentstack"
  />

  <!-- Art direction for different devices -->
  <NuxtPicture
    :src="article.featured_image.url"
    :imgAttrs="{ alt: article.title }"
    sizes="100vw md:50vw"
    :modifiers="{
      auto: 'webp,compress',
      quality: 85,
    }"
    provider="contentstack"
  />
</template>

优点

  • 自动图像优化,支持 WebP 和压缩
  • 响应式图像,支持尺寸和密度
  • Contentstack 转换,通过图像交付 API
  • 懒加载和性能优化
  • 艺术方向支持,通过 NuxtPicture
  • 开发者体验 - 熟悉的 @nuxt/image API

可用修饰符

Contentstack 提供程序支持所有标准 @nuxt/image 修饰符以及 Contentstack 特定的转换

// Common modifiers
:modifiers="{
  // Image optimization
  auto: 'webp,compress',
  quality: 90,

  // Dimensions and cropping
  width: 800,
  height: 400,
  fit: 'cover', // crop, bounds, fill, scale

  // Effects
  blur: 5,
  brightness: 110,
  contrast: 120,
  saturation: 130,

  // Format
  format: 'webp',
}"

useImageTransform 可组合项

对于需要对图像转换进行编程控制的高级用例,您可以使用 useImageTransform 可组合项。当您需要动态应用转换或使用常规 <img> 标签而不是 <NuxtImg> 时,这尤其有用。

const { transformedUrl, updateTransform, resetTransform } = useImageTransform(
  originalImageUrl,
  {
    width: 800,
    height: 600,
    quality: 80,
    format: "webp",
    fit: "crop"
  }
);

// Use in template
<img :src="transformedUrl" alt="Transformed image" />

// Update transforms reactively
updateTransform({ width: 1200, quality: 90 });

// Advanced transforms
const { transformedUrl: advancedUrl } = useImageTransform(imageUrl, {
  width: 800,
  height: 600,
  quality: 85,
  format: "webp",
  overlay: {
    relativeURL: "/watermark.png",
    align: "bottom-right",
    width: "20p" // 20% of base image
  },
  sharpen: {
    amount: 5,
    radius: 2,
    threshold: 0
  },
  saturation: 10,
  brightness: 5
});

何时使用 useImageTransform<NuxtImg>

  • 在大多数情况下使用 <NuxtImg> - 它经过优化,支持响应式图像,并处理懒加载
  • 当您需要动态转换、使用常规 <img> 标签或构建自定义图像组件时,使用 useImageTransform

支持的图像转换

  • 尺寸widthheightdpr(设备像素比)
  • 质量和格式qualityformatauto 优化
  • 裁剪fitcroptrim
  • 效果blursaturationbrightnesscontrastsharpen
  • 叠加:添加水印或其他图像
  • 高级orientpadbg-colorframeresizeFilter

贡献

本地开发
# Install dependencies
npm install

# Generate type stubs
npm run dev:prepare

# Develop with the playground
npm run dev

# Build the playground
npm run dev:build

# Run ESLint
npm run lint

# Run Vitest
npm run test
npm run test:watch

# Release new version
npm run release