refactor: reorganize package structure and decouple framework packages (#338)
* refactor: reorganize package structure and decouple framework packages ## Package Structure Reorganization - Reorganized 55 packages into categorized subdirectories: - packages/framework/ - Generic framework (Laya/Cocos compatible) - packages/engine/ - ESEngine core modules - packages/rendering/ - Rendering modules (WASM dependent) - packages/physics/ - Physics modules - packages/streaming/ - World streaming - packages/network-ext/ - Network extensions - packages/editor/ - Editor framework and plugins - packages/rust/ - Rust WASM engine - packages/tools/ - Build tools and SDK ## Framework Package Decoupling - Decoupled behavior-tree and blueprint packages from ESEngine dependencies - Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent) - ESEngine-specific code moved to esengine/ subpath exports - Framework packages now usable with Cocos/Laya without ESEngine ## CI Configuration - Updated CI to only type-check and lint framework packages - Added type-check:framework and lint:framework scripts ## Breaking Changes - Package import paths changed due to directory reorganization - ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine') * fix: update es-engine file path after directory reorganization * docs: update README to focus on framework over engine * ci: only build framework packages, remove Rust/WASM dependencies * fix: remove esengine subpath from behavior-tree and blueprint builds ESEngine integration code will only be available in full engine builds. Framework packages are now purely engine-agnostic. * fix: move network-protocols to framework, build both in CI * fix: update workflow paths from packages/core to packages/framework/core * fix: exclude esengine folder from type-check in behavior-tree and blueprint * fix: update network tsconfig references to new paths * fix: add test:ci:framework to only test framework packages in CI * fix: only build core and math npm packages in CI * fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
48
packages/editor/editor-runtime/package.json
Normal file
48
packages/editor/editor-runtime/package.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "@esengine/editor-runtime",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "dist/editor-runtime.js",
|
||||
"module": "dist/editor-runtime.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/editor-runtime.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc && vite build",
|
||||
"build:watch": "vite build --watch",
|
||||
"clean": "rimraf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@esengine/asset-system": "workspace:*",
|
||||
"@tauri-apps/api": "^2.2.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.4.0",
|
||||
"@tauri-apps/plugin-fs": "^2.4.2",
|
||||
"tsyringe": "^4.10.0",
|
||||
"reflect-metadata": "^0.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@esengine/ecs-framework": "workspace:*",
|
||||
"@esengine/ecs-framework-math": "workspace:*",
|
||||
"@esengine/editor-core": "workspace:*",
|
||||
"@esengine/engine-core": "workspace:*",
|
||||
"@esengine/fairygui": "workspace:*",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"zustand": "^5.0.8",
|
||||
"lucide-react": "^0.545.0",
|
||||
"rimraf": "^5.0.0",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^6.0.7",
|
||||
"vite-plugin-dts": "^4.5.0"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
121
packages/editor/editor-runtime/src/FileSystem.ts
Normal file
121
packages/editor/editor-runtime/src/FileSystem.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* 文件系统 API
|
||||
* File System API
|
||||
*
|
||||
* 提供统一的文件读写接口,通过后端 Tauri 命令实现。
|
||||
* 所有插件应使用这些 API 而非直接调用 @tauri-apps/plugin-fs。
|
||||
*/
|
||||
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
|
||||
/**
|
||||
* 文件系统操作封装
|
||||
* 使用后端命令避免前端权限问题
|
||||
*/
|
||||
export const FileSystem = {
|
||||
/**
|
||||
* 读取文本文件
|
||||
* @param path 文件路径
|
||||
* @returns 文件内容
|
||||
*/
|
||||
async readTextFile(path: string): Promise<string> {
|
||||
return await invoke<string>('read_file_content', { path });
|
||||
},
|
||||
|
||||
/**
|
||||
* 写入文本文件
|
||||
* @param path 文件路径
|
||||
* @param content 文件内容
|
||||
*/
|
||||
async writeTextFile(path: string, content: string): Promise<void> {
|
||||
await invoke('write_file_content', { path, content });
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查路径是否存在
|
||||
* @param path 文件或目录路径
|
||||
*/
|
||||
async exists(path: string): Promise<boolean> {
|
||||
return await invoke<boolean>('path_exists', { path });
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建目录
|
||||
* @param path 目录路径
|
||||
*/
|
||||
async createDirectory(path: string): Promise<void> {
|
||||
await invoke('create_directory', { path });
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建空文件
|
||||
* @param path 文件路径
|
||||
*/
|
||||
async createFile(path: string): Promise<void> {
|
||||
await invoke('create_file', { path });
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
* @param path 文件路径
|
||||
*/
|
||||
async deleteFile(path: string): Promise<void> {
|
||||
await invoke('delete_file', { path });
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除目录
|
||||
* @param path 目录路径
|
||||
*/
|
||||
async deleteDirectory(path: string): Promise<void> {
|
||||
await invoke('delete_folder', { path });
|
||||
},
|
||||
|
||||
/**
|
||||
* 重命名文件或目录
|
||||
* @param oldPath 原路径
|
||||
* @param newPath 新路径
|
||||
*/
|
||||
async rename(oldPath: string, newPath: string): Promise<void> {
|
||||
await invoke('rename_file_or_folder', { oldPath, newPath });
|
||||
},
|
||||
|
||||
/**
|
||||
* 读取目录内容
|
||||
* @param path 目录路径
|
||||
* @returns 目录项列表
|
||||
*/
|
||||
async readDirectory(path: string): Promise<DirectoryEntry[]> {
|
||||
return await invoke<DirectoryEntry[]>('read_directory', { path });
|
||||
},
|
||||
|
||||
/**
|
||||
* 读取文件为 Base64
|
||||
* @param path 文件路径
|
||||
* @returns Base64 编码的内容
|
||||
*/
|
||||
async readFileAsBase64(path: string): Promise<string> {
|
||||
return await invoke<string>('read_file_as_base64', { filePath: path });
|
||||
},
|
||||
|
||||
/**
|
||||
* 写入二进制文件
|
||||
* @param path 文件路径
|
||||
* @param data 二进制数据
|
||||
*/
|
||||
async writeBinaryFile(path: string, data: Uint8Array): Promise<void> {
|
||||
await invoke('write_binary_file', { filePath: path, content: Array.from(data) });
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 目录项
|
||||
*/
|
||||
export interface DirectoryEntry {
|
||||
name: string;
|
||||
path: string;
|
||||
isDirectory: boolean;
|
||||
isFile: boolean;
|
||||
size?: number;
|
||||
modified?: number;
|
||||
}
|
||||
155
packages/editor/editor-runtime/src/PluginAPI.ts
Normal file
155
packages/editor/editor-runtime/src/PluginAPI.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* Plugin API - 为插件提供简洁的访问接口
|
||||
* Plugin API - Provides simple access interface for plugins
|
||||
*
|
||||
* 使用方式 | Usage:
|
||||
* ```typescript
|
||||
* import { PluginAPI } from '@esengine/editor-runtime';
|
||||
*
|
||||
* const scene = PluginAPI.scene;
|
||||
* const entityStore = PluginAPI.entityStore;
|
||||
* const messageHub = PluginAPI.messageHub;
|
||||
*
|
||||
* // 使用 ServiceToken 获取服务(类型安全)| Get service with ServiceToken (type-safe)
|
||||
* import { AssetManagerToken } from '@esengine/asset-system';
|
||||
* const assetManager = PluginAPI.resolve(AssetManagerToken);
|
||||
* ```
|
||||
*
|
||||
* 这个 API 会自动从全局 __ESENGINE_SDK__ 获取正确的实例,
|
||||
* 避免模块实例不一致的问题。
|
||||
* This API automatically gets correct instances from global __ESENGINE_SDK__,
|
||||
* avoiding module instance inconsistency issues.
|
||||
*/
|
||||
|
||||
import type { EntityStoreService, MessageHub } from '@esengine/editor-core';
|
||||
import type { Scene, ServiceContainer, ServiceToken } from '@esengine/ecs-framework';
|
||||
|
||||
/**
|
||||
* 核心服务接口
|
||||
* Core service interface
|
||||
*
|
||||
* 定义内部 Core 提供的服务访问接口。
|
||||
* Defines service access interface provided by internal Core.
|
||||
*/
|
||||
interface ICoreServices {
|
||||
services: ServiceContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部 API 接口定义
|
||||
* Internal API interface definition
|
||||
*
|
||||
* 定义全局 __ESENGINE_SDK__.api 提供的方法。
|
||||
* Defines methods provided by global __ESENGINE_SDK__.api.
|
||||
*/
|
||||
interface IPluginAPIInternal {
|
||||
/** 获取当前场景 | Get current scene */
|
||||
getScene(): Scene | null;
|
||||
/** 获取实体存储服务 | Get entity store service */
|
||||
getEntityStore(): EntityStoreService;
|
||||
/** 获取消息总线 | Get message hub */
|
||||
getMessageHub(): MessageHub;
|
||||
/**
|
||||
* 解析服务(类型安全)
|
||||
* Resolve service (type-safe)
|
||||
* @param token 服务令牌 | Service token
|
||||
*/
|
||||
resolveService<T>(token: ServiceToken<T>): T;
|
||||
/** 获取核心实例 | Get core instance */
|
||||
getCore(): ICoreServices;
|
||||
}
|
||||
|
||||
// 声明全局类型
|
||||
declare global {
|
||||
interface Window {
|
||||
__ESENGINE_SDK__?: {
|
||||
api?: IPluginAPIInternal;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内部 API
|
||||
*/
|
||||
function getInternalAPI(): IPluginAPIInternal {
|
||||
const api = window.__ESENGINE_SDK__?.api;
|
||||
if (!api) {
|
||||
throw new Error('[PluginAPI] 插件 API 未初始化,请确保编辑器已正确启动');
|
||||
}
|
||||
return api;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件 API
|
||||
* 提供简洁的属性访问方式,避免模块实例不一致问题
|
||||
*/
|
||||
export const PluginAPI = {
|
||||
/**
|
||||
* 获取当前场景
|
||||
* @throws 如果场景未初始化
|
||||
*/
|
||||
get scene(): Scene {
|
||||
const scene = getInternalAPI().getScene();
|
||||
if (!scene) {
|
||||
throw new Error('[PluginAPI] 场景未初始化,请先打开或创建一个场景');
|
||||
}
|
||||
return scene;
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取当前场景(可能为 null)
|
||||
*/
|
||||
get sceneOrNull(): Scene | null {
|
||||
return getInternalAPI().getScene();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取 EntityStoreService
|
||||
*/
|
||||
get entityStore(): EntityStoreService {
|
||||
return getInternalAPI().getEntityStore();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取 MessageHub
|
||||
*/
|
||||
get messageHub(): MessageHub {
|
||||
return getInternalAPI().getMessageHub();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取服务容器
|
||||
*/
|
||||
get services(): ServiceContainer {
|
||||
return getInternalAPI().getCore().services;
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析服务(类型安全)
|
||||
* Resolve service (type-safe)
|
||||
*
|
||||
* 使用 ServiceToken 获取服务实例,提供完整的类型推断。
|
||||
* Use ServiceToken to get service instance with full type inference.
|
||||
*
|
||||
* @param token 服务令牌 | Service token
|
||||
* @returns 服务实例 | Service instance
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { AssetManagerToken } from '@esengine/asset-system';
|
||||
* const assetManager = PluginAPI.resolve(AssetManagerToken);
|
||||
* // assetManager 类型自动推断为 IAssetManager
|
||||
* ```
|
||||
*/
|
||||
resolve<T>(token: ServiceToken<T>): T {
|
||||
return getInternalAPI().resolveService<T>(token);
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查 API 是否可用
|
||||
*/
|
||||
get isAvailable(): boolean {
|
||||
return !!window.__ESENGINE_SDK__?.api;
|
||||
},
|
||||
};
|
||||
238
packages/editor/editor-runtime/src/i18n/createPluginLocale.ts
Normal file
238
packages/editor/editor-runtime/src/i18n/createPluginLocale.ts
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* Plugin Locale Factory
|
||||
* 插件国际化工厂
|
||||
*
|
||||
* Provides utilities for plugins to create their own locale hooks
|
||||
* that integrate with the central LocaleService.
|
||||
*
|
||||
* 为插件提供创建本地化 hook 的工具函数,
|
||||
* 这些 hook 会与中央 LocaleService 集成。
|
||||
*/
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { LocaleService, type Locale, type TranslationParams } from '@esengine/editor-core';
|
||||
import { Core } from '@esengine/ecs-framework';
|
||||
|
||||
/**
|
||||
* Translation object structure
|
||||
* 翻译对象结构
|
||||
*/
|
||||
export type Translations = {
|
||||
[key: string]: string | Translations;
|
||||
};
|
||||
|
||||
/**
|
||||
* Plugin translations bundle
|
||||
* 插件翻译包
|
||||
*/
|
||||
export interface PluginTranslationsBundle<T extends Translations = Translations> {
|
||||
en: T;
|
||||
zh: T;
|
||||
es?: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return type of usePluginLocale hook
|
||||
* usePluginLocale hook 的返回类型
|
||||
*/
|
||||
export interface PluginLocaleHook {
|
||||
/** Translation function | 翻译函数 */
|
||||
t: (key: string, params?: TranslationParams) => string;
|
||||
/** Current locale | 当前语言 */
|
||||
locale: Locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get nested value from object by dot-separated path
|
||||
* 通过点分隔路径从对象获取嵌套值
|
||||
*/
|
||||
function getNestedValue(obj: Translations, key: string): string | undefined {
|
||||
const keys = key.split('.');
|
||||
let current: unknown = obj;
|
||||
|
||||
for (const k of keys) {
|
||||
if (current && typeof current === 'object' && k in current) {
|
||||
current = (current as Record<string, unknown>)[k];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return typeof current === 'string' ? current : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolate parameters into translation string
|
||||
* 将参数插入翻译字符串
|
||||
*/
|
||||
function interpolate(text: string, params?: TranslationParams): string {
|
||||
if (!params) return text;
|
||||
|
||||
return text.replace(/\{\{(\w+)\}\}/g, (_, key) => {
|
||||
const value = params[key];
|
||||
return value !== undefined ? String(value) : `{{${key}}}`;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a locale hook for a plugin with its own translations
|
||||
* 为插件创建一个带有自己翻译的 locale hook
|
||||
*
|
||||
* This factory creates a React hook that:
|
||||
* 1. Syncs with the central LocaleService for locale changes
|
||||
* 2. Uses plugin-specific translations
|
||||
* 3. Falls back to English if translation not found
|
||||
*
|
||||
* 这个工厂创建一个 React hook,它会:
|
||||
* 1. 与中央 LocaleService 同步语言变化
|
||||
* 2. 使用插件特定的翻译
|
||||
* 3. 如果找不到翻译则回退到英语
|
||||
*
|
||||
* @param translations - Plugin translations bundle | 插件翻译包
|
||||
* @returns A React hook for accessing translations | 用于访问翻译的 React hook
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // In your plugin's hooks folder:
|
||||
* // 在你的插件 hooks 文件夹中:
|
||||
* import { createPluginLocale } from '@esengine/editor-runtime';
|
||||
* import { en, zh, es } from '../locales';
|
||||
*
|
||||
* export const useTilemapLocale = createPluginLocale({ en, zh, es });
|
||||
*
|
||||
* // In your components:
|
||||
* // 在你的组件中:
|
||||
* const { t, locale } = useTilemapLocale();
|
||||
* return <button>{t('toolbar.save')}</button>;
|
||||
* ```
|
||||
*/
|
||||
export function createPluginLocale<T extends Translations>(
|
||||
translations: PluginTranslationsBundle<T>
|
||||
): () => PluginLocaleHook {
|
||||
const allTranslations = {
|
||||
en: translations.en,
|
||||
zh: translations.zh,
|
||||
es: translations.es || translations.en // Fallback to English if no Spanish
|
||||
};
|
||||
|
||||
return function usePluginLocale(): PluginLocaleHook {
|
||||
const [locale, setLocale] = useState<Locale>('en');
|
||||
|
||||
useEffect(() => {
|
||||
// Try to get LocaleService and sync with it
|
||||
// 尝试获取 LocaleService 并与之同步
|
||||
try {
|
||||
const localeService = Core.services.tryResolve(LocaleService);
|
||||
if (localeService) {
|
||||
setLocale(localeService.getCurrentLocale());
|
||||
|
||||
// Subscribe to locale changes
|
||||
// 订阅语言变化
|
||||
return localeService.onChange((newLocale) => {
|
||||
setLocale(newLocale);
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// LocaleService not available, use default
|
||||
// LocaleService 不可用,使用默认值
|
||||
}
|
||||
}, []);
|
||||
|
||||
const t = useCallback((key: string, params?: TranslationParams): string => {
|
||||
const currentTranslations = allTranslations[locale] || allTranslations.en;
|
||||
const value = getNestedValue(currentTranslations as Translations, key);
|
||||
|
||||
if (value) {
|
||||
return interpolate(value, params);
|
||||
}
|
||||
|
||||
// Fallback to English if current locale doesn't have the key
|
||||
// 如果当前语言没有该键,回退到英语
|
||||
if (locale !== 'en') {
|
||||
const enValue = getNestedValue(allTranslations.en as Translations, key);
|
||||
if (enValue) {
|
||||
return interpolate(enValue, params);
|
||||
}
|
||||
}
|
||||
|
||||
// Return key itself as last resort
|
||||
// 最后返回键本身
|
||||
return key;
|
||||
}, [locale]);
|
||||
|
||||
return { t, locale };
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a non-React translation function for a plugin
|
||||
* 为插件创建一个非 React 翻译函数
|
||||
*
|
||||
* Use this for translating in non-React contexts (services, utilities, etc.)
|
||||
* 在非 React 上下文(服务、工具类等)中使用此函数进行翻译
|
||||
*
|
||||
* @param translations - Plugin translations bundle | 插件翻译包
|
||||
* @returns A translation function | 翻译函数
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Create translator
|
||||
* // 创建翻译器
|
||||
* const translate = createPluginTranslator({ en, zh, es });
|
||||
*
|
||||
* // Use in non-React code
|
||||
* // 在非 React 代码中使用
|
||||
* const message = translate('errors.notFound', 'en');
|
||||
* ```
|
||||
*/
|
||||
export function createPluginTranslator<T extends Translations>(
|
||||
translations: PluginTranslationsBundle<T>
|
||||
): (key: string, locale?: Locale, params?: TranslationParams) => string {
|
||||
const allTranslations = {
|
||||
en: translations.en,
|
||||
zh: translations.zh,
|
||||
es: translations.es || translations.en
|
||||
};
|
||||
|
||||
return function translate(
|
||||
key: string,
|
||||
locale: Locale = 'en',
|
||||
params?: TranslationParams
|
||||
): string {
|
||||
const currentTranslations = allTranslations[locale] || allTranslations.en;
|
||||
const value = getNestedValue(currentTranslations as Translations, key);
|
||||
|
||||
if (value) {
|
||||
return interpolate(value, params);
|
||||
}
|
||||
|
||||
if (locale !== 'en') {
|
||||
const enValue = getNestedValue(allTranslations.en as Translations, key);
|
||||
if (enValue) {
|
||||
return interpolate(enValue, params);
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current locale from LocaleService
|
||||
* 从 LocaleService 获取当前语言
|
||||
*
|
||||
* Use this in non-React contexts where you need the current locale
|
||||
* 在需要当前语言的非 React 上下文中使用
|
||||
*
|
||||
* @returns Current locale or 'en' as default | 当前语言或默认 'en'
|
||||
*/
|
||||
export function getCurrentLocale(): Locale {
|
||||
try {
|
||||
const localeService = Core.services.tryResolve(LocaleService);
|
||||
if (localeService) {
|
||||
return localeService.getCurrentLocale();
|
||||
}
|
||||
} catch {
|
||||
// LocaleService not available
|
||||
}
|
||||
return 'en';
|
||||
}
|
||||
19
packages/editor/editor-runtime/src/i18n/index.ts
Normal file
19
packages/editor/editor-runtime/src/i18n/index.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Plugin i18n Infrastructure
|
||||
* 插件国际化基础设施
|
||||
*
|
||||
* Exports utilities for plugins to create their own locale systems
|
||||
* that integrate with the central LocaleService.
|
||||
*
|
||||
* 导出供插件创建自己的本地化系统的工具,
|
||||
* 这些系统会与中央 LocaleService 集成。
|
||||
*/
|
||||
|
||||
export {
|
||||
createPluginLocale,
|
||||
createPluginTranslator,
|
||||
getCurrentLocale,
|
||||
type Translations,
|
||||
type PluginTranslationsBundle,
|
||||
type PluginLocaleHook
|
||||
} from './createPluginLocale';
|
||||
403
packages/editor/editor-runtime/src/index.ts
Normal file
403
packages/editor/editor-runtime/src/index.ts
Normal file
@@ -0,0 +1,403 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
// =============================================================================
|
||||
// React
|
||||
// =============================================================================
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
export { React, ReactDOM };
|
||||
|
||||
export {
|
||||
useState,
|
||||
useEffect,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
useContext,
|
||||
useReducer,
|
||||
useLayoutEffect,
|
||||
useImperativeHandle,
|
||||
useDebugValue,
|
||||
useDeferredValue,
|
||||
useTransition,
|
||||
useId,
|
||||
useSyncExternalStore,
|
||||
useInsertionEffect,
|
||||
createContext,
|
||||
forwardRef,
|
||||
memo,
|
||||
lazy,
|
||||
Suspense,
|
||||
Fragment,
|
||||
StrictMode,
|
||||
createElement,
|
||||
cloneElement,
|
||||
isValidElement,
|
||||
Children,
|
||||
createRef,
|
||||
Component as ReactComponent,
|
||||
PureComponent
|
||||
} from 'react';
|
||||
|
||||
export type {
|
||||
FC,
|
||||
ReactNode,
|
||||
ReactElement,
|
||||
ComponentType,
|
||||
ComponentProps,
|
||||
PropsWithChildren,
|
||||
RefObject,
|
||||
MutableRefObject,
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
CSSProperties,
|
||||
MouseEvent,
|
||||
KeyboardEvent,
|
||||
ChangeEvent,
|
||||
FormEvent,
|
||||
FocusEvent,
|
||||
DragEvent
|
||||
} from 'react';
|
||||
|
||||
// =============================================================================
|
||||
// State Management
|
||||
// =============================================================================
|
||||
export { create as createStore } from 'zustand';
|
||||
export type { StoreApi, UseBoundStore } from 'zustand';
|
||||
|
||||
// =============================================================================
|
||||
// Dependency Injection
|
||||
// =============================================================================
|
||||
export { container, injectable, singleton, inject } from 'tsyringe';
|
||||
export type { DependencyContainer } from 'tsyringe';
|
||||
|
||||
// =============================================================================
|
||||
// ECS Framework Core
|
||||
// =============================================================================
|
||||
export * from '@esengine/ecs-framework';
|
||||
|
||||
// =============================================================================
|
||||
// Engine Core (Sorting Layers, Input, etc.)
|
||||
// =============================================================================
|
||||
export {
|
||||
SortingLayers,
|
||||
SortingLayerManager,
|
||||
sortingLayerManager,
|
||||
DEFAULT_SORTING_LAYERS,
|
||||
Input,
|
||||
InputManager,
|
||||
InputSystem,
|
||||
MouseButton as EngineMouseButton,
|
||||
} from '@esengine/engine-core';
|
||||
|
||||
export type {
|
||||
ISortable,
|
||||
SortingLayerConfig,
|
||||
SortingLayerName,
|
||||
ISortingLayerManager,
|
||||
InputSystemConfig,
|
||||
KeyState,
|
||||
MouseButtonState,
|
||||
} from '@esengine/engine-core';
|
||||
|
||||
// Re-export vector interfaces from math
|
||||
export type { IVector2, IVector3 } from '@esengine/ecs-framework-math';
|
||||
|
||||
// =============================================================================
|
||||
// Editor Core
|
||||
// Rename conflicting exports to avoid collision with ecs-framework
|
||||
// =============================================================================
|
||||
export {
|
||||
EditorComponentRegistry,
|
||||
} from '@esengine/editor-core';
|
||||
|
||||
export type {
|
||||
IEventBus as IEditorEventBus,
|
||||
PluginState as EditorPluginState,
|
||||
PropertyControl as EditorPropertyControl,
|
||||
PropertyType as EditorPropertyType,
|
||||
} from '@esengine/editor-core';
|
||||
|
||||
// Runtime exports from editor-core
|
||||
export {
|
||||
// Enums
|
||||
PanelPosition,
|
||||
UIExtensionType,
|
||||
|
||||
// Classes
|
||||
UIRegistry,
|
||||
MessageHub,
|
||||
SerializerRegistry,
|
||||
EntityStoreService,
|
||||
LocaleService,
|
||||
ProjectService,
|
||||
ComponentDiscoveryService,
|
||||
LogService,
|
||||
SettingsRegistry,
|
||||
SceneManagerService,
|
||||
FileActionRegistry,
|
||||
EntityCreationRegistry,
|
||||
CompilerRegistry,
|
||||
CommandManager,
|
||||
InspectorRegistry,
|
||||
PropertyRendererRegistry,
|
||||
FieldEditorRegistry,
|
||||
ComponentActionRegistry,
|
||||
BaseCommand,
|
||||
PropertyMetadataService,
|
||||
|
||||
// Plugin system
|
||||
PluginManager,
|
||||
|
||||
// Gizmo exports
|
||||
GizmoRegistry,
|
||||
GizmoColors,
|
||||
hasGizmoProvider,
|
||||
hexToGizmoColor,
|
||||
isGizmoProviderRegistered,
|
||||
|
||||
// Symbols (用于跨包插件访问)
|
||||
IFileSystemService,
|
||||
IDialogService,
|
||||
IMessageHub,
|
||||
ICompilerRegistry,
|
||||
IInspectorRegistry,
|
||||
IFileActionRegistry,
|
||||
} from '@esengine/editor-core';
|
||||
|
||||
// Type-only exports from editor-core
|
||||
export type {
|
||||
// Plugin types
|
||||
ISerializer,
|
||||
FileCreationTemplate,
|
||||
FileActionHandler,
|
||||
RegisteredPlugin,
|
||||
PluginConfig,
|
||||
LoadingPhase,
|
||||
PluginState,
|
||||
|
||||
// Service interfaces
|
||||
IFileSystem,
|
||||
FileEntry,
|
||||
IDialog,
|
||||
INotification,
|
||||
IInspectorProvider,
|
||||
InspectorContext,
|
||||
IPropertyRenderer,
|
||||
IFieldEditor,
|
||||
FieldEditorContext,
|
||||
ICompiler,
|
||||
CompileResult,
|
||||
CompilerContext,
|
||||
ICommand,
|
||||
IEditorDataStore,
|
||||
|
||||
// Module interfaces
|
||||
ICommandRegistry,
|
||||
IPanelRegistry,
|
||||
IModuleContext,
|
||||
IEditorModule,
|
||||
Unsubscribe,
|
||||
|
||||
// Gizmo types
|
||||
GizmoType,
|
||||
GizmoColor,
|
||||
IRectGizmoData,
|
||||
ICircleGizmoData,
|
||||
ILineGizmoData,
|
||||
IGridGizmoData,
|
||||
IGizmoRenderData,
|
||||
IGizmoProvider,
|
||||
GizmoProviderFn,
|
||||
GizmoProviderRegistration,
|
||||
|
||||
// UI types
|
||||
MenuItem,
|
||||
ToolbarItem,
|
||||
PanelDescriptor,
|
||||
EntityCreationTemplate,
|
||||
IFileAPI,
|
||||
PropertyMetadata,
|
||||
MenuItemDescriptor,
|
||||
ToolbarItemDescriptor,
|
||||
ComponentAction,
|
||||
|
||||
// Plugin system types
|
||||
IEditorModuleLoader,
|
||||
IEditorPlugin,
|
||||
ModuleManifest,
|
||||
ModuleCategory,
|
||||
ModulePlatform,
|
||||
ModuleExports,
|
||||
IRuntimePlugin,
|
||||
IRuntimeModule,
|
||||
SystemContext,
|
||||
ComponentInspectorProviderDef,
|
||||
} from '@esengine/editor-core';
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// Tauri API (低级 API,建议使用 FileSystem 封装)
|
||||
// =============================================================================
|
||||
export { invoke, convertFileSrc } from '@tauri-apps/api/core';
|
||||
export { open, save, message, ask, confirm } from '@tauri-apps/plugin-dialog';
|
||||
// 注意:以下 API 可能有权限问题,建议使用 FileSystem 替代
|
||||
export {
|
||||
readTextFile,
|
||||
writeTextFile,
|
||||
readDir,
|
||||
exists,
|
||||
mkdir,
|
||||
remove,
|
||||
rename,
|
||||
copyFile
|
||||
} from '@tauri-apps/plugin-fs';
|
||||
|
||||
// =============================================================================
|
||||
// FileSystem API (推荐使用)
|
||||
// 通过后端命令实现,避免前端权限问题
|
||||
// =============================================================================
|
||||
export { FileSystem, type DirectoryEntry } from './FileSystem';
|
||||
|
||||
// =============================================================================
|
||||
// Icons (Lucide React)
|
||||
// =============================================================================
|
||||
import * as Icons from 'lucide-react';
|
||||
export { Icons };
|
||||
export type { LucideIcon } from 'lucide-react';
|
||||
|
||||
// =============================================================================
|
||||
// Plugin API
|
||||
// =============================================================================
|
||||
export { PluginAPI } from './PluginAPI';
|
||||
|
||||
// =============================================================================
|
||||
// FairyGUI System
|
||||
// =============================================================================
|
||||
export {
|
||||
// ECS Integration
|
||||
FGUIComponent,
|
||||
FGUIRenderSystem,
|
||||
getFGUIRenderSystem,
|
||||
setFGUIRenderSystem,
|
||||
FGUIRuntimeModule,
|
||||
FGUIPlugin,
|
||||
// Core
|
||||
GObject,
|
||||
GComponent,
|
||||
GRoot,
|
||||
GGroup,
|
||||
Controller,
|
||||
Transition,
|
||||
Timer,
|
||||
Stage,
|
||||
EScaleMode,
|
||||
EAlignMode,
|
||||
UIConfig,
|
||||
getUIConfig,
|
||||
setUIConfig,
|
||||
UIObjectFactory,
|
||||
GObjectPool,
|
||||
DragDropManager,
|
||||
// Widgets
|
||||
GImage,
|
||||
GTextField,
|
||||
GGraph,
|
||||
GButton,
|
||||
GProgressBar,
|
||||
GSlider,
|
||||
GLoader,
|
||||
GList,
|
||||
GTextInput,
|
||||
EKeyboardType,
|
||||
PopupMenu,
|
||||
Window,
|
||||
// Package
|
||||
UIPackage,
|
||||
PackageItem,
|
||||
// Events
|
||||
EventDispatcher,
|
||||
FGUIEvents,
|
||||
// Render
|
||||
RenderCollector,
|
||||
RenderBridge,
|
||||
Canvas2DBackend,
|
||||
FGUIRenderDataProvider,
|
||||
createFGUIRenderDataProvider,
|
||||
// Tween
|
||||
GTween,
|
||||
GTweener,
|
||||
TweenManager,
|
||||
TweenValue,
|
||||
evaluateEase,
|
||||
// Asset
|
||||
FUIAssetLoader,
|
||||
fuiAssetLoader,
|
||||
// Field Types
|
||||
EButtonMode,
|
||||
EAutoSizeType,
|
||||
EAlignType,
|
||||
EVertAlignType,
|
||||
ELoaderFillType,
|
||||
EListLayoutType,
|
||||
EListSelectionMode,
|
||||
EOverflowType,
|
||||
EPackageItemType,
|
||||
EObjectType,
|
||||
EProgressTitleType,
|
||||
EScrollBarDisplayType,
|
||||
EScrollType,
|
||||
EFlipType,
|
||||
EChildrenRenderOrder,
|
||||
EGroupLayoutType,
|
||||
EPopupDirection,
|
||||
ERelationType,
|
||||
EFillMethod,
|
||||
EFillOrigin,
|
||||
EObjectPropID,
|
||||
EGearType,
|
||||
EEaseType,
|
||||
EBlendMode,
|
||||
ETransitionActionType,
|
||||
EGraphType,
|
||||
} from '@esengine/fairygui';
|
||||
|
||||
export type {
|
||||
// FairyGUI types
|
||||
IFGUIComponentData,
|
||||
RenderSubmitCallback,
|
||||
ItemRenderer,
|
||||
ItemProvider,
|
||||
IUISource,
|
||||
TypedEventListener,
|
||||
EventListener,
|
||||
FGUIEventType,
|
||||
IEventContext,
|
||||
IInputEventData,
|
||||
IFUIAsset,
|
||||
IAssetLoader,
|
||||
IAssetContent,
|
||||
IAssetParseContext,
|
||||
IEngineRenderData,
|
||||
IFGUIRenderDataProvider,
|
||||
TextureResolverFn,
|
||||
TweenCallback,
|
||||
} from '@esengine/fairygui';
|
||||
|
||||
// =============================================================================
|
||||
// Plugin i18n Infrastructure
|
||||
// =============================================================================
|
||||
export {
|
||||
createPluginLocale,
|
||||
createPluginTranslator,
|
||||
getCurrentLocale,
|
||||
type Translations as PluginTranslations,
|
||||
type PluginTranslationsBundle,
|
||||
type PluginLocaleHook
|
||||
} from './i18n';
|
||||
|
||||
// =============================================================================
|
||||
// SDK Metadata
|
||||
// =============================================================================
|
||||
export const SDK_VERSION = '1.0.0';
|
||||
export const SDK_NAME = '@esengine/editor-runtime';
|
||||
37
packages/editor/editor-runtime/tsconfig.json
Normal file
37
packages/editor/editor-runtime/tsconfig.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ES2020",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": [
|
||||
"ES2020",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"composite": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../framework/core"
|
||||
},
|
||||
{
|
||||
"path": "../../editor/editor-core"
|
||||
}
|
||||
]
|
||||
}
|
||||
63
packages/editor/editor-runtime/vite.config.ts
Normal file
63
packages/editor/editor-runtime/vite.config.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import { resolve } from 'path';
|
||||
import dts from 'vite-plugin-dts';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
dts({
|
||||
include: ['src'],
|
||||
outDir: 'dist',
|
||||
rollupTypes: false,
|
||||
tsconfigPath: './tsconfig.json'
|
||||
})
|
||||
],
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify('production')
|
||||
},
|
||||
esbuild: {
|
||||
// 保留类名,用于跨包插件服务匹配
|
||||
keepNames: true,
|
||||
},
|
||||
build: {
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'src/index.ts'),
|
||||
formats: ['es'],
|
||||
fileName: () => 'editor-runtime.js'
|
||||
},
|
||||
rollupOptions: {
|
||||
// 将 React 和核心 ECS 框架设为外部依赖
|
||||
// 这确保整个应用使用同一个 ComponentRegistry 实例
|
||||
external: [
|
||||
'react',
|
||||
'react-dom',
|
||||
'react/jsx-runtime',
|
||||
'@esengine/ecs-framework',
|
||||
'@esengine/engine-core', // TransformComponent 等核心组件
|
||||
'@esengine/ecs-components',
|
||||
'@esengine/tilemap',
|
||||
'@esengine/fairygui', // FairyGUI system
|
||||
'@esengine/behavior-tree',
|
||||
'@esengine/platform-web',
|
||||
'@esengine/ecs-engine-bindgen',
|
||||
'@esengine/asset-system',
|
||||
'@esengine/sprite', // SpriteComponent
|
||||
'@esengine/camera', // CameraComponent
|
||||
'@esengine/material-system',
|
||||
'@esengine/editor-core', // 编辑器核心(避免 runtime-core → ecs-engine-bindgen 传递依赖)
|
||||
],
|
||||
output: {
|
||||
exports: 'named',
|
||||
inlineDynamicImports: true,
|
||||
// 映射外部依赖到全局变量
|
||||
globals: {
|
||||
'react': 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
'react/jsx-runtime': 'ReactJSXRuntime'
|
||||
}
|
||||
}
|
||||
},
|
||||
target: 'es2020',
|
||||
minify: false,
|
||||
sourcemap: true
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user