@ygo-build-x/plugin-sdk
v0.1.0
Published
YGO Build X 插件开发 SDK - 为游戏王卡组构筑工具开发插件
Downloads
15
Maintainers
Readme
YGO Build X 插件系统
概述
YGO Build X 插件系统允许开发者扩展应用功能,支持侧边栏面板和独立页面两种展示模式。
快速开始
1. 安装依赖
npm install @ygo-build-x/plugin-sdk2. 创建插件
import { definePlugin } from '@ygo-build-x/plugin-sdk'
import MyComponent from './MyComponent.vue'
export default definePlugin({
meta: {
id: '@your-name/plugin-name',
name: '插件名称',
version: '1.0.0',
description: '插件描述',
author: 'Your Name',
icon: '🎯',
type: 'calculator',
permissions: ['cards.read', 'ui.sidebar'],
displayMode: 'sidebar',
},
setup(context) {
return {
views: [
{
id: 'my-view',
title: '我的视图',
component: MyComponent,
displayMode: 'sidebar',
sidebar: { icon: '🎯', order: 10 },
},
],
}
},
})插件类型
| 类型 | 说明 |
| ------------- | ----------------------------- |
| calculator | 计算器类 (概率计算、分析工具) |
| tool | 工具类 (导入导出、数据处理) |
| view | 视图类 (新页面、面板) |
| theme | 主题类 (样式、外观) |
| integration | 集成类 (第三方服务) |
展示模式
| 模式 | 说明 |
| --------- | ---------------- |
| sidebar | 在侧边栏显示面板 |
| page | 作为独立路由页面 |
权限系统
可用权限
| 权限 | 风险等级 | 说明 |
| ------------ | -------- | -------------- |
| cards.read | 低 | 读取卡片数据库 |
| deck.read | 中 | 读取用户卡组 |
| deck.write | 高 | 修改用户卡组 |
| storage | 低 | 本地存储数据 |
| network | 高 | 访问网络 |
| ui.notify | 低 | 显示通知消息 |
| ui.modal | 中 | 显示弹窗 |
| ui.sidebar | 低 | 在侧边栏显示 |
| ui.route | 中 | 添加新页面 |
权限请求
插件安装时会请求所需权限,用户可以选择授予或拒绝部分权限。
Context API
cards - 卡片数据 API
// 获取单张卡片
const card = await context.cards.getById(12345)
// 批量获取卡片 (最多 500 张)
const cards = await context.cards.getByIds([12345, 67890])
// 搜索卡片
const results = await context.cards.search('灰流丽', { limit: 10 })deck - 卡组数据 API
// 获取当前卡组(包含主卡组、额外卡组、副卡组)
const deck = context.deck.getCurrent()
console.log(deck.main) // 主卡组卡片 ID 数组
console.log(deck.extra) // 额外卡组卡片 ID 数组
console.log(deck.side) // 副卡组卡片 ID 数组
// 获取所有卡组列表
const decks = context.deck.getList()
// 监听卡组变化
const unsubscribe = context.deck.onChange(deck => {
console.log('卡组已更新:', deck)
})storage - 存储 API
// 存储数据
context.storage.set('my-key', { data: 'value' })
// 读取数据
const data = context.storage.get<MyData>('my-key')
// 删除数据
context.storage.remove('my-key')
// 清空插件存储
context.storage.clear()ui - 界面 API
// 显示通知
context.ui.notify({
type: 'success', // 'success' | 'info' | 'warning' | 'error'
message: '操作成功',
duration: 3000,
})
// 显示确认框
const confirmed = await context.ui.confirm({
title: '确认操作',
message: '确定要执行此操作吗?',
confirmText: '确定',
cancelText: '取消',
})events - 事件 API
// 监听事件
const unsubscribe = context.events.on('deck:changed', deck => {
console.log('卡组变化:', deck)
})
// 触发自定义事件 (仅在插件命名空间内)
context.events.emit('my-event', { data: 'value' })可用事件
| 事件 | 说明 |
| ----------------------- | ------------ |
| deck:changed | 卡组内容变化 |
| deck:saved | 卡组已保存 |
| deck:loaded | 卡组已加载 |
| card:selected | 卡片被选中 |
| calculation:started | 开始计算 |
| calculation:completed | 计算完成 |
扩展点
views - 视图扩展
{
views: [{
id: 'unique-view-id',
title: '视图标题',
component: VueComponent,
displayMode: 'sidebar', // 'sidebar' | 'page'
sidebar: { // 仅 sidebar 模式
icon: '🎯',
order: 10,
group: 'calculator', // 'calculator' | 'tool' | 'other'
},
route: { // 仅 page 模式
path: '/my-page',
meta: { title: '页面标题' },
},
}],
}calculators - 计算器扩展
{
calculators: [{
id: 'my-calculator',
name: '我的计算器',
description: '计算器描述',
icon: '🧮',
component: CalculatorComponent,
}],
}menuItems - 菜单项扩展
{
menuItems: [{
id: 'my-menu',
label: '菜单项',
icon: '📁',
location: 'sidebar', // 'sidebar' | 'toolbar' | 'context'
order: 10,
onClick: () => { /* 处理点击 */ },
viewId: 'my-view', // 关联的视图 ID
}],
}importFormats / exportFormats - 导入导出格式
{
importFormats: [{
id: 'my-format',
name: 'My Format',
extensions: ['.myf'],
parse: async (content) => [/* 返回卡片 ID 数组 */],
}],
exportFormats: [{
id: 'my-format',
name: 'My Format',
extension: '.myf',
generate: async (cardIds) => '导出内容',
}],
}安全机制
代码隔离
- 插件代码在 Web Worker 沙箱中执行
- 阻止访问危险全局对象 (eval, Function, document 等)
- 原型链冻结保护
签名验证
- RSA-2048 数字签名
- SHA-256 代码哈希验证
- 常量时间比较 (防时序攻击)
资源限制
- 执行超时: 30 秒
- API 调用限制: 每秒 100 次
- 存储配额: 5MB/插件
- 事件监听器: 50 个/插件
网络代理
- 域名白名单/黑名单
- 内网地址阻止
- 请求频率限制
- 敏感头过滤
内容安全策略 (CSP)
default-src 'self';
script-src 'self' 'unsafe-inline' blob:;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self';
frame-ancestors 'none';Vue 组件开发
获取插件上下文
<script setup lang="ts">
import { inject } from 'vue'
import type { PluginContext } from '@ygo-build-x/plugin-sdk'
const context = inject<PluginContext>('pluginContext')
const pluginId = inject<string>('pluginId')
// 使用 context API
const deck = context?.deck.getCurrent()
</script>UI 样式规范
插件应遵循 YGO Build X 的 Notion 风格设计哲学:简洁、清爽、内容优先。
CSS 变量
使用项目提供的 CSS 变量而非硬编码值:
/* ✅ 正确 */
.my-element {
background: var(--bg-secondary);
color: var(--text-primary);
border: 1px solid var(--border);
padding: var(--space-3);
border-radius: var(--radius-md);
transition: all var(--transition-normal);
}
/* ❌ 错误 */
.my-element {
background: #f7f6f3;
color: #37352f;
padding: 12px;
}可用 CSS 变量
背景色:
--bg-primary: #ffffff- 主要背景--bg-secondary: #f7f6f3- 次要背景--bg-tertiary: #f1f1ef- 第三级背景--bg-hover: rgba(55, 53, 47, 0.08)- 悬停背景--bg-active: rgba(55, 53, 47, 0.16)- 激活背景
文字色:
--text-primary: #37352f- 主要文字--text-secondary: #6b6b6b- 次要文字--text-tertiary: #9b9a97- 第三级文字--text-placeholder: rgba(55, 53, 47, 0.5)- 占位符
强调色:
--accent: #2eaadc- 主强调色--accent-hover: #2496c4- 悬停强调色--accent-light: rgba(46, 170, 220, 0.15)- 浅强调色背景
语义色:
--success: #0f7b6c/--success-light- 成功--warning: #d9730d/--warning-light- 警告--error: #e03e3e/--error-light- 错误--info: #2eaadc/--info-light- 信息
边框:
--border: rgba(55, 53, 47, 0.09)- 细边框--border-strong: rgba(55, 53, 47, 0.16)- 强边框
间距:
--space-1: 4px~--space-12: 48px
圆角:
--radius-sm: 3px--radius-md: 6px--radius-lg: 8px--radius-xl: 12px
过渡:
--transition-fast: 0.1s ease--transition-normal: 0.15s ease--transition-slow: 0.2s ease
样式禁忌
- ❌ 纯黑文字
#000000 - ❌ 重阴影或发光效果
- ❌ 渐变背景
- ❌ 饱和度过高的背景色
- ❌ 大圆角 (> 8px) 用于容器
- ❌ 粗边框 (> 1px)
- ❌ 闪烁或弹跳动画
- ❌
!important声明
安全注意事项
禁止使用:
v-html指令 (XSS 风险)getCurrentInstance()(可能逃逸)- 直接 DOM 操作
window.open(),window.locationeval(),new Function()
推荐做法:
- 使用 Context API 访问数据
- 使用 Naive UI 组件库
- 数据验证和错误处理
插件元数据
interface PluginMeta {
id: string // 唯一标识 (@scope/name 格式)
name: string // 显示名称
version: string // 版本号 (semver)
description: string // 描述
author:
| string
| {
// 作者
name: string
email?: string
url?: string
}
icon?: string // 图标 (emoji 或 URL)
homepage?: string // 主页
repository?: string // 仓库
keywords?: string[] // 关键词
minHostVersion?: string // 最低宿主版本
type: PluginType // 插件类型
permissions: PluginPermission[] // 所需权限
displayMode?: PluginDisplayMode // 展示模式
}生命周期
definePlugin({
meta: {
/* ... */
},
// 插件启用时调用
setup(context) {
console.log('插件已启用')
// 返回扩展点
return {
views: [
/* ... */
],
}
},
// 插件禁用时调用
teardown() {
console.log('插件已禁用')
// 清理资源
},
})类型定义
完整类型定义请参考 @ygo-build-x/plugin-sdk/types.ts
示例插件
小世界计算器
import { definePlugin } from '@ygo-build-x/plugin-sdk'
import SmallWorldPlugin from './SmallWorldPlugin.vue'
export default definePlugin({
meta: {
id: '@ygo-build-x/small-world',
name: '小世界计算器',
version: '1.0.0',
description: '分析怪兽之间的桥接关系',
author: 'YGO Build X',
icon: '🌐',
type: 'calculator',
permissions: ['cards.read', 'deck.read', 'storage', 'ui.sidebar', 'ui.notify'],
displayMode: 'sidebar',
},
setup() {
return {
views: [
{
id: 'small-world-view',
title: '小世界计算器',
component: SmallWorldPlugin,
displayMode: 'sidebar',
sidebar: { icon: '🌐', order: 10, group: 'calculator' },
},
],
}
},
})