feat(blueprint): 蓝图组合系统 (#326)

* feat(blueprint): 添加蓝图组合系统

- 添加 IBlueprintFragment 接口和实现,支持可重用蓝图片段
- 添加 IBlueprintComposer 接口和实现,支持片段组合
- 添加 FragmentRegistry 片段注册表
- 支持暴露引脚连接和编译成完整蓝图
- 支持片段资产序列化格式

* fix(blueprint): 移除未使用的 ExposedPin 导入
This commit is contained in:
YHH
2025-12-25 12:42:57 +08:00
committed by GitHub
parent ebb984d354
commit 0bf849e193
5 changed files with 1194 additions and 0 deletions

View File

@@ -0,0 +1,575 @@
/**
* @zh 蓝图组合器接口和实现
* @en Blueprint Composer Interface and Implementation
*
* @zh 将多个蓝图片段组合成一个完整的蓝图
* @en Composes multiple blueprint fragments into a complete blueprint
*/
import type { BlueprintAsset, BlueprintVariable } from '../types/blueprint';
import type { BlueprintNode, BlueprintConnection } from '../types/nodes';
import type { IBlueprintFragment } from './BlueprintFragment';
// =============================================================================
// 槽位定义 | Slot Definition
// =============================================================================
/**
* @zh 片段槽位
* @en Fragment slot
*
* @zh 组合器中放置片段的位置
* @en A position in the composer where a fragment is placed
*/
export interface FragmentSlot {
/**
* @zh 槽位 ID
* @en Slot ID
*/
readonly id: string;
/**
* @zh 槽位名称
* @en Slot name
*/
readonly name: string;
/**
* @zh 放置的片段
* @en Placed fragment
*/
readonly fragment: IBlueprintFragment;
/**
* @zh 在组合图中的位置偏移
* @en Position offset in the composed graph
*/
readonly position: { x: number; y: number };
}
/**
* @zh 槽位间连接
* @en Connection between slots
*/
export interface SlotConnection {
/**
* @zh 连接 ID
* @en Connection ID
*/
readonly id: string;
/**
* @zh 源槽位 ID
* @en Source slot ID
*/
readonly fromSlotId: string;
/**
* @zh 源引脚名称
* @en Source pin name
*/
readonly fromPin: string;
/**
* @zh 目标槽位 ID
* @en Target slot ID
*/
readonly toSlotId: string;
/**
* @zh 目标引脚名称
* @en Target pin name
*/
readonly toPin: string;
}
// =============================================================================
// 组合器接口 | Composer Interface
// =============================================================================
/**
* @zh 蓝图组合器接口
* @en Blueprint composer interface
*
* @zh 用于将多个蓝图片段组合成一个完整蓝图
* @en Used to compose multiple blueprint fragments into a complete blueprint
*/
export interface IBlueprintComposer {
/**
* @zh 组合器名称
* @en Composer name
*/
readonly name: string;
/**
* @zh 获取所有槽位
* @en Get all slots
*/
getSlots(): FragmentSlot[];
/**
* @zh 获取所有连接
* @en Get all connections
*/
getConnections(): SlotConnection[];
/**
* @zh 添加片段到槽位
* @en Add fragment to slot
*
* @param fragment - @zh 蓝图片段 @en Blueprint fragment
* @param slotId - @zh 槽位 ID @en Slot ID
* @param options - @zh 选项 @en Options
*/
addFragment(
fragment: IBlueprintFragment,
slotId: string,
options?: {
name?: string;
position?: { x: number; y: number };
}
): void;
/**
* @zh 移除槽位
* @en Remove slot
*
* @param slotId - @zh 槽位 ID @en Slot ID
*/
removeSlot(slotId: string): void;
/**
* @zh 连接两个槽位的引脚
* @en Connect pins between two slots
*
* @param fromSlotId - @zh 源槽位 ID @en Source slot ID
* @param fromPin - @zh 源引脚名称 @en Source pin name
* @param toSlotId - @zh 目标槽位 ID @en Target slot ID
* @param toPin - @zh 目标引脚名称 @en Target pin name
*/
connect(
fromSlotId: string,
fromPin: string,
toSlotId: string,
toPin: string
): void;
/**
* @zh 断开连接
* @en Disconnect
*
* @param connectionId - @zh 连接 ID @en Connection ID
*/
disconnect(connectionId: string): void;
/**
* @zh 验证组合是否有效
* @en Validate if the composition is valid
*/
validate(): CompositionValidationResult;
/**
* @zh 编译成蓝图资产
* @en Compile into blueprint asset
*/
compile(): BlueprintAsset;
/**
* @zh 清空组合器
* @en Clear the composer
*/
clear(): void;
}
// =============================================================================
// 验证结果 | Validation Result
// =============================================================================
/**
* @zh 组合验证结果
* @en Composition validation result
*/
export interface CompositionValidationResult {
/**
* @zh 是否有效
* @en Whether valid
*/
readonly isValid: boolean;
/**
* @zh 错误列表
* @en Error list
*/
readonly errors: CompositionError[];
/**
* @zh 警告列表
* @en Warning list
*/
readonly warnings: CompositionWarning[];
}
/**
* @zh 组合错误
* @en Composition error
*/
export interface CompositionError {
readonly type: 'missing-connection' | 'type-mismatch' | 'cycle-detected' | 'invalid-slot';
readonly message: string;
readonly slotId?: string;
readonly pinName?: string;
}
/**
* @zh 组合警告
* @en Composition warning
*/
export interface CompositionWarning {
readonly type: 'unused-output' | 'unconnected-input';
readonly message: string;
readonly slotId?: string;
readonly pinName?: string;
}
// =============================================================================
// 组合器实现 | Composer Implementation
// =============================================================================
/**
* @zh 蓝图组合器实现
* @en Blueprint composer implementation
*/
export class BlueprintComposer implements IBlueprintComposer {
readonly name: string;
private slots: Map<string, FragmentSlot> = new Map();
private connections: Map<string, SlotConnection> = new Map();
private connectionIdCounter = 0;
constructor(name: string) {
this.name = name;
}
getSlots(): FragmentSlot[] {
return Array.from(this.slots.values());
}
getConnections(): SlotConnection[] {
return Array.from(this.connections.values());
}
addFragment(
fragment: IBlueprintFragment,
slotId: string,
options?: {
name?: string;
position?: { x: number; y: number };
}
): void {
if (this.slots.has(slotId)) {
throw new Error(`Slot '${slotId}' already exists`);
}
const slot: FragmentSlot = {
id: slotId,
name: options?.name ?? fragment.name,
fragment,
position: options?.position ?? { x: 0, y: 0 }
};
this.slots.set(slotId, slot);
}
removeSlot(slotId: string): void {
if (!this.slots.has(slotId)) {
return;
}
// Remove all connections involving this slot
const toRemove: string[] = [];
for (const [id, conn] of this.connections) {
if (conn.fromSlotId === slotId || conn.toSlotId === slotId) {
toRemove.push(id);
}
}
for (const id of toRemove) {
this.connections.delete(id);
}
this.slots.delete(slotId);
}
connect(
fromSlotId: string,
fromPin: string,
toSlotId: string,
toPin: string
): void {
const fromSlot = this.slots.get(fromSlotId);
const toSlot = this.slots.get(toSlotId);
if (!fromSlot) {
throw new Error(`Source slot '${fromSlotId}' not found`);
}
if (!toSlot) {
throw new Error(`Target slot '${toSlotId}' not found`);
}
const fromPinDef = fromSlot.fragment.outputs.find(p => p.name === fromPin);
const toPinDef = toSlot.fragment.inputs.find(p => p.name === toPin);
if (!fromPinDef) {
throw new Error(`Output pin '${fromPin}' not found in slot '${fromSlotId}'`);
}
if (!toPinDef) {
throw new Error(`Input pin '${toPin}' not found in slot '${toSlotId}'`);
}
const connectionId = `conn_${++this.connectionIdCounter}`;
const connection: SlotConnection = {
id: connectionId,
fromSlotId,
fromPin,
toSlotId,
toPin
};
this.connections.set(connectionId, connection);
}
disconnect(connectionId: string): void {
this.connections.delete(connectionId);
}
validate(): CompositionValidationResult {
const errors: CompositionError[] = [];
const warnings: CompositionWarning[] = [];
// Check for required inputs without connections
for (const slot of this.slots.values()) {
for (const input of slot.fragment.inputs) {
const hasConnection = Array.from(this.connections.values()).some(
c => c.toSlotId === slot.id && c.toPin === input.name
);
if (!hasConnection && input.defaultValue === undefined) {
warnings.push({
type: 'unconnected-input',
message: `Input '${input.name}' in slot '${slot.id}' is not connected`,
slotId: slot.id,
pinName: input.name
});
}
}
// Check for unused outputs
for (const output of slot.fragment.outputs) {
const hasConnection = Array.from(this.connections.values()).some(
c => c.fromSlotId === slot.id && c.fromPin === output.name
);
if (!hasConnection) {
warnings.push({
type: 'unused-output',
message: `Output '${output.name}' in slot '${slot.id}' is not connected`,
slotId: slot.id,
pinName: output.name
});
}
}
}
// Check type compatibility
for (const conn of this.connections.values()) {
const fromSlot = this.slots.get(conn.fromSlotId);
const toSlot = this.slots.get(conn.toSlotId);
if (!fromSlot || !toSlot) {
errors.push({
type: 'invalid-slot',
message: `Invalid slot reference in connection '${conn.id}'`
});
continue;
}
const fromPinDef = fromSlot.fragment.outputs.find(p => p.name === conn.fromPin);
const toPinDef = toSlot.fragment.inputs.find(p => p.name === conn.toPin);
if (fromPinDef && toPinDef && fromPinDef.type !== toPinDef.type) {
if (fromPinDef.type !== 'any' && toPinDef.type !== 'any') {
errors.push({
type: 'type-mismatch',
message: `Type mismatch: '${fromPinDef.type}' -> '${toPinDef.type}' in connection '${conn.id}'`
});
}
}
}
return {
isValid: errors.length === 0,
errors,
warnings
};
}
compile(): BlueprintAsset {
const nodes: BlueprintNode[] = [];
const connections: BlueprintConnection[] = [];
const variables: BlueprintVariable[] = [];
const nodeIdMap = new Map<string, Map<string, string>>();
// Copy nodes from each fragment with new IDs
let nodeIdCounter = 0;
for (const slot of this.slots.values()) {
const slotNodeMap = new Map<string, string>();
nodeIdMap.set(slot.id, slotNodeMap);
for (const node of slot.fragment.graph.nodes) {
const newNodeId = `node_${++nodeIdCounter}`;
slotNodeMap.set(node.id, newNodeId);
nodes.push({
...node,
id: newNodeId,
position: {
x: node.position.x + slot.position.x,
y: node.position.y + slot.position.y
}
});
}
// Copy internal connections
for (const conn of slot.fragment.graph.connections) {
const newFromId = slotNodeMap.get(conn.fromNodeId);
const newToId = slotNodeMap.get(conn.toNodeId);
if (newFromId && newToId) {
connections.push({
...conn,
id: `conn_internal_${connections.length}`,
fromNodeId: newFromId,
toNodeId: newToId
});
}
}
// Copy variables (with slot prefix to avoid conflicts)
for (const variable of slot.fragment.graph.variables) {
variables.push({
...variable,
name: `${slot.id}_${variable.name}`
});
}
}
// Create connections between slots based on exposed pins
for (const slotConn of this.connections.values()) {
const fromSlot = this.slots.get(slotConn.fromSlotId);
const toSlot = this.slots.get(slotConn.toSlotId);
if (!fromSlot || !toSlot) continue;
const fromPinDef = fromSlot.fragment.outputs.find(p => p.name === slotConn.fromPin);
const toPinDef = toSlot.fragment.inputs.find(p => p.name === slotConn.toPin);
if (!fromPinDef || !toPinDef) continue;
const fromNodeMap = nodeIdMap.get(slotConn.fromSlotId);
const toNodeMap = nodeIdMap.get(slotConn.toSlotId);
if (!fromNodeMap || !toNodeMap) continue;
const fromNodeId = fromNodeMap.get(fromPinDef.internalNodeId);
const toNodeId = toNodeMap.get(toPinDef.internalNodeId);
if (fromNodeId && toNodeId) {
connections.push({
id: `conn_slot_${connections.length}`,
fromNodeId,
fromPin: fromPinDef.internalPinName,
toNodeId,
toPin: toPinDef.internalPinName
});
}
}
return {
version: 1,
type: 'blueprint',
metadata: {
name: this.name,
description: `Composed from ${this.slots.size} fragments`,
createdAt: Date.now(),
modifiedAt: Date.now()
},
variables,
nodes,
connections
};
}
clear(): void {
this.slots.clear();
this.connections.clear();
this.connectionIdCounter = 0;
}
}
// =============================================================================
// 工厂函数 | Factory Functions
// =============================================================================
/**
* @zh 创建蓝图组合器
* @en Create blueprint composer
*/
export function createComposer(name: string): IBlueprintComposer {
return new BlueprintComposer(name);
}
// =============================================================================
// 组合资产格式 | Composition Asset Format
// =============================================================================
/**
* @zh 蓝图组合资产格式
* @en Blueprint composition asset format
*/
export interface BlueprintCompositionAsset {
/**
* @zh 格式版本
* @en Format version
*/
version: number;
/**
* @zh 资产类型标识
* @en Asset type identifier
*/
type: 'blueprint-composition';
/**
* @zh 组合名称
* @en Composition name
*/
name: string;
/**
* @zh 槽位数据
* @en Slot data
*/
slots: Array<{
id: string;
name: string;
fragmentId: string;
position: { x: number; y: number };
}>;
/**
* @zh 连接数据
* @en Connection data
*/
connections: SlotConnection[];
}

View File

@@ -0,0 +1,351 @@
/**
* @zh 蓝图片段接口和实现
* @en Blueprint Fragment Interface and Implementation
*
* @zh 定义可重用的蓝图片段,用于组合系统
* @en Defines reusable blueprint fragments for the composition system
*/
import type { BlueprintAsset } from '../types/blueprint';
import type { BlueprintPinType } from '../types/pins';
// =============================================================================
// 暴露引脚定义 | Exposed Pin Definition
// =============================================================================
/**
* @zh 暴露引脚定义
* @en Exposed pin definition
*
* @zh 片段对外暴露的引脚,可与其他片段连接
* @en Pins exposed by the fragment that can be connected to other fragments
*/
export interface ExposedPin {
/**
* @zh 引脚名称
* @en Pin name
*/
readonly name: string;
/**
* @zh 显示名称
* @en Display name
*/
readonly displayName: string;
/**
* @zh 引脚类型
* @en Pin type
*/
readonly type: BlueprintPinType;
/**
* @zh 引脚方向
* @en Pin direction
*/
readonly direction: 'input' | 'output';
/**
* @zh 描述
* @en Description
*/
readonly description?: string;
/**
* @zh 默认值(仅输入引脚)
* @en Default value (input pins only)
*/
readonly defaultValue?: unknown;
/**
* @zh 关联的内部节点 ID
* @en Associated internal node ID
*/
readonly internalNodeId: string;
/**
* @zh 关联的内部引脚名称
* @en Associated internal pin name
*/
readonly internalPinName: string;
}
// =============================================================================
// 蓝图片段接口 | Blueprint Fragment Interface
// =============================================================================
/**
* @zh 蓝图片段接口
* @en Blueprint fragment interface
*
* @zh 代表一个可重用的蓝图逻辑单元,如技能、卡牌效果等
* @en Represents a reusable unit of blueprint logic, such as skills, card effects, etc.
*/
export interface IBlueprintFragment {
/**
* @zh 片段唯一标识
* @en Fragment unique identifier
*/
readonly id: string;
/**
* @zh 片段名称
* @en Fragment name
*/
readonly name: string;
/**
* @zh 片段描述
* @en Fragment description
*/
readonly description?: string;
/**
* @zh 片段分类
* @en Fragment category
*/
readonly category?: string;
/**
* @zh 片段标签
* @en Fragment tags
*/
readonly tags?: string[];
/**
* @zh 暴露的输入引脚
* @en Exposed input pins
*/
readonly inputs: ExposedPin[];
/**
* @zh 暴露的输出引脚
* @en Exposed output pins
*/
readonly outputs: ExposedPin[];
/**
* @zh 内部蓝图图
* @en Internal blueprint graph
*/
readonly graph: BlueprintAsset;
/**
* @zh 片段版本
* @en Fragment version
*/
readonly version?: string;
/**
* @zh 图标名称
* @en Icon name
*/
readonly icon?: string;
/**
* @zh 颜色(用于可视化)
* @en Color (for visualization)
*/
readonly color?: string;
}
// =============================================================================
// 蓝图片段实现 | Blueprint Fragment Implementation
// =============================================================================
/**
* @zh 蓝图片段配置
* @en Blueprint fragment configuration
*/
export interface BlueprintFragmentConfig {
id: string;
name: string;
description?: string;
category?: string;
tags?: string[];
inputs?: ExposedPin[];
outputs?: ExposedPin[];
graph: BlueprintAsset;
version?: string;
icon?: string;
color?: string;
}
/**
* @zh 蓝图片段实现
* @en Blueprint fragment implementation
*/
export class BlueprintFragment implements IBlueprintFragment {
readonly id: string;
readonly name: string;
readonly description?: string;
readonly category?: string;
readonly tags?: string[];
readonly inputs: ExposedPin[];
readonly outputs: ExposedPin[];
readonly graph: BlueprintAsset;
readonly version?: string;
readonly icon?: string;
readonly color?: string;
constructor(config: BlueprintFragmentConfig) {
this.id = config.id;
this.name = config.name;
this.description = config.description;
this.category = config.category;
this.tags = config.tags;
this.inputs = config.inputs ?? [];
this.outputs = config.outputs ?? [];
this.graph = config.graph;
this.version = config.version;
this.icon = config.icon;
this.color = config.color;
}
/**
* @zh 获取所有暴露引脚
* @en Get all exposed pins
*/
getAllExposedPins(): ExposedPin[] {
return [...this.inputs, ...this.outputs];
}
/**
* @zh 通过名称查找输入引脚
* @en Find input pin by name
*/
findInput(name: string): ExposedPin | undefined {
return this.inputs.find(p => p.name === name);
}
/**
* @zh 通过名称查找输出引脚
* @en Find output pin by name
*/
findOutput(name: string): ExposedPin | undefined {
return this.outputs.find(p => p.name === name);
}
}
// =============================================================================
// 工厂函数 | Factory Functions
// =============================================================================
/**
* @zh 创建暴露引脚
* @en Create exposed pin
*/
export function createExposedPin(
name: string,
type: BlueprintPinType,
direction: 'input' | 'output',
internalNodeId: string,
internalPinName: string,
options?: {
displayName?: string;
description?: string;
defaultValue?: unknown;
}
): ExposedPin {
return {
name,
displayName: options?.displayName ?? name,
type,
direction,
description: options?.description,
defaultValue: options?.defaultValue,
internalNodeId,
internalPinName
};
}
/**
* @zh 创建蓝图片段
* @en Create blueprint fragment
*/
export function createFragment(config: BlueprintFragmentConfig): IBlueprintFragment {
return new BlueprintFragment(config);
}
// =============================================================================
// 片段资产格式 | Fragment Asset Format
// =============================================================================
/**
* @zh 蓝图片段资产格式
* @en Blueprint fragment asset format
*
* @zh 用于序列化和反序列化片段
* @en Used for serializing and deserializing fragments
*/
export interface BlueprintFragmentAsset {
/**
* @zh 格式版本
* @en Format version
*/
version: number;
/**
* @zh 资产类型标识
* @en Asset type identifier
*/
type: 'blueprint-fragment';
/**
* @zh 片段数据
* @en Fragment data
*/
fragment: {
id: string;
name: string;
description?: string;
category?: string;
tags?: string[];
inputs: ExposedPin[];
outputs: ExposedPin[];
version?: string;
icon?: string;
color?: string;
};
/**
* @zh 内部蓝图图
* @en Internal blueprint graph
*/
graph: BlueprintAsset;
}
/**
* @zh 从资产创建片段
* @en Create fragment from asset
*/
export function fragmentFromAsset(asset: BlueprintFragmentAsset): IBlueprintFragment {
return new BlueprintFragment({
...asset.fragment,
graph: asset.graph
});
}
/**
* @zh 将片段转为资产
* @en Convert fragment to asset
*/
export function fragmentToAsset(fragment: IBlueprintFragment): BlueprintFragmentAsset {
return {
version: 1,
type: 'blueprint-fragment',
fragment: {
id: fragment.id,
name: fragment.name,
description: fragment.description,
category: fragment.category,
tags: fragment.tags,
inputs: fragment.inputs,
outputs: fragment.outputs,
version: fragment.version,
icon: fragment.icon,
color: fragment.color
},
graph: fragment.graph
};
}

View File

@@ -0,0 +1,208 @@
/**
* @zh 片段注册表
* @en Fragment Registry
*
* @zh 管理和查询蓝图片段
* @en Manages and queries blueprint fragments
*/
import type { IBlueprintFragment } from './BlueprintFragment';
// =============================================================================
// 片段注册表接口 | Fragment Registry Interface
// =============================================================================
/**
* @zh 片段过滤器
* @en Fragment filter
*/
export interface FragmentFilter {
/**
* @zh 按分类过滤
* @en Filter by category
*/
category?: string;
/**
* @zh 按标签过滤(任意匹配)
* @en Filter by tags (any match)
*/
tags?: string[];
/**
* @zh 按名称搜索
* @en Search by name
*/
search?: string;
}
/**
* @zh 片段注册表接口
* @en Fragment registry interface
*/
export interface IFragmentRegistry {
/**
* @zh 注册片段
* @en Register fragment
*/
register(fragment: IBlueprintFragment): void;
/**
* @zh 注销片段
* @en Unregister fragment
*/
unregister(id: string): void;
/**
* @zh 获取片段
* @en Get fragment
*/
get(id: string): IBlueprintFragment | undefined;
/**
* @zh 检查片段是否存在
* @en Check if fragment exists
*/
has(id: string): boolean;
/**
* @zh 获取所有片段
* @en Get all fragments
*/
getAll(): IBlueprintFragment[];
/**
* @zh 按条件过滤片段
* @en Filter fragments by criteria
*/
filter(filter: FragmentFilter): IBlueprintFragment[];
/**
* @zh 获取所有分类
* @en Get all categories
*/
getCategories(): string[];
/**
* @zh 获取所有标签
* @en Get all tags
*/
getTags(): string[];
/**
* @zh 清空注册表
* @en Clear registry
*/
clear(): void;
}
// =============================================================================
// 片段注册表实现 | Fragment Registry Implementation
// =============================================================================
/**
* @zh 片段注册表实现
* @en Fragment registry implementation
*/
export class FragmentRegistry implements IFragmentRegistry {
private fragments: Map<string, IBlueprintFragment> = new Map();
register(fragment: IBlueprintFragment): void {
if (this.fragments.has(fragment.id)) {
console.warn(`Fragment '${fragment.id}' already registered, overwriting`);
}
this.fragments.set(fragment.id, fragment);
}
unregister(id: string): void {
this.fragments.delete(id);
}
get(id: string): IBlueprintFragment | undefined {
return this.fragments.get(id);
}
has(id: string): boolean {
return this.fragments.has(id);
}
getAll(): IBlueprintFragment[] {
return Array.from(this.fragments.values());
}
filter(filter: FragmentFilter): IBlueprintFragment[] {
let results = this.getAll();
if (filter.category) {
results = results.filter(f => f.category === filter.category);
}
if (filter.tags && filter.tags.length > 0) {
results = results.filter(f =>
f.tags && filter.tags!.some(t => f.tags!.includes(t))
);
}
if (filter.search) {
const searchLower = filter.search.toLowerCase();
results = results.filter(f =>
f.name.toLowerCase().includes(searchLower) ||
f.description?.toLowerCase().includes(searchLower)
);
}
return results;
}
getCategories(): string[] {
const categories = new Set<string>();
for (const fragment of this.fragments.values()) {
if (fragment.category) {
categories.add(fragment.category);
}
}
return Array.from(categories).sort();
}
getTags(): string[] {
const tags = new Set<string>();
for (const fragment of this.fragments.values()) {
if (fragment.tags) {
for (const tag of fragment.tags) {
tags.add(tag);
}
}
}
return Array.from(tags).sort();
}
clear(): void {
this.fragments.clear();
}
/**
* @zh 获取片段数量
* @en Get fragment count
*/
get size(): number {
return this.fragments.size;
}
}
// =============================================================================
// 单例实例 | Singleton Instance
// =============================================================================
/**
* @zh 默认片段注册表实例
* @en Default fragment registry instance
*/
export const defaultFragmentRegistry = new FragmentRegistry();
/**
* @zh 创建片段注册表
* @en Create fragment registry
*/
export function createFragmentRegistry(): IFragmentRegistry {
return new FragmentRegistry();
}

View File

@@ -0,0 +1,57 @@
/**
* @zh 蓝图组合系统导出
* @en Blueprint Composition System Export
*/
// =============================================================================
// 片段 | Fragment
// =============================================================================
export type {
ExposedPin,
IBlueprintFragment,
BlueprintFragmentConfig,
BlueprintFragmentAsset
} from './BlueprintFragment';
export {
BlueprintFragment,
createExposedPin,
createFragment,
fragmentFromAsset,
fragmentToAsset
} from './BlueprintFragment';
// =============================================================================
// 组合器 | Composer
// =============================================================================
export type {
FragmentSlot,
SlotConnection,
IBlueprintComposer,
CompositionValidationResult,
CompositionError,
CompositionWarning,
BlueprintCompositionAsset
} from './BlueprintComposer';
export {
BlueprintComposer,
createComposer
} from './BlueprintComposer';
// =============================================================================
// 注册表 | Registry
// =============================================================================
export type {
FragmentFilter,
IFragmentRegistry
} from './FragmentRegistry';
export {
FragmentRegistry,
defaultFragmentRegistry,
createFragmentRegistry
} from './FragmentRegistry';

View File

@@ -12,6 +12,9 @@ export * from './runtime';
// Triggers // Triggers
export * from './triggers'; export * from './triggers';
// Composition
export * from './composition';
// Nodes (import to register) // Nodes (import to register)
import './nodes'; import './nodes';