From 107439d70cc9b83b83d11b50ef48d5c7476c3ab5 Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Thu, 27 Nov 2025 20:42:46 +0800 Subject: [PATCH] Feature/runtime cdn and plugin loader (#240) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(ui): 完善 UI 布局系统和编辑器可视化工具 * refactor: 移除 ModuleRegistry,统一使用 PluginManager 插件系统 * fix: 修复 CodeQL 警告并提升测试覆盖率 * refactor: 分离运行时入口点,解决 runtime bundle 包含 React 的问题 * fix(ci): 添加 editor-core 和 editor-runtime 到 CI 依赖构建步骤 * docs: 完善 ServiceContainer 文档,新增 Symbol.for 模式和 @InjectProperty 说明 * fix(ci): 修复 type-check 失败问题 * fix(ci): 修复类型检查失败问题 * fix(ci): 修复类型检查失败问题 * fix(ci): behavior-tree 构建添加 @tauri-apps 外部依赖 * fix(ci): behavior-tree 添加 @tauri-apps/plugin-fs 类型依赖 * fix(ci): platform-web 添加缺失的 behavior-tree 依赖 * fix(lint): 移除正则表达式中不必要的转义字符 --- .github/workflows/ci.yml | 5 + .github/workflows/release-editor.yml | 31 +- docs/guide/service-container.md | 234 +- packages/behavior-tree-editor/package.json | 48 - packages/behavior-tree-editor/pnpm-lock.yaml | 2039 ----------------- .../behavior-tree-editor/rollup.config.cjs | 67 - .../behavior-tree-editor/scripts/copy-css.js | 25 - .../src/BehaviorTreeModule.ts | 105 - .../src/BehaviorTreePlugin.ts | 172 -- packages/behavior-tree-editor/src/index.ts | 42 - packages/behavior-tree-editor/tsconfig.json | 29 - packages/behavior-tree/package.json | 82 +- packages/behavior-tree/plugin.json | 30 + .../behavior-tree/src/BehaviorTreeBuilder.ts | 2 +- .../behavior-tree/src/BehaviorTreePlugin.ts | 89 - .../src/BehaviorTreeRuntimeModule.ts | 43 + .../behavior-tree/src/BehaviorTreeStarter.ts | 6 +- .../EditorToBehaviorTreeDataConverter.ts | 2 +- .../src/Serialization/NodeTemplates.ts | 2 +- .../src/editor/BehaviorTreePlugin.ts | 98 + .../src/editor}/PluginContext.ts | 0 .../application/commands/CommandManager.ts | 0 .../editor}/application/commands/ICommand.ts | 0 .../application/commands/ITreeState.ts | 0 .../commands/tree/AddConnectionCommand.ts | 2 +- .../commands/tree/CreateNodeCommand.ts | 2 +- .../commands/tree/DeleteNodeCommand.ts | 2 +- .../commands/tree/MoveNodeCommand.ts | 2 +- .../commands/tree/RemoveConnectionCommand.ts | 2 +- .../commands/tree/UpdateNodeDataCommand.ts | 2 +- .../application/commands/tree/index.ts | 0 .../application/interfaces/IExecutionHooks.ts | 0 .../application/services/BlackboardManager.ts | 0 .../services/ExecutionController.ts | 0 .../services/GlobalBlackboardService.ts | 2 +- .../state/BehaviorTreeDataStore.ts | 2 +- .../use-cases/AddConnectionUseCase.ts | 2 +- .../use-cases/CreateNodeUseCase.ts | 4 +- .../use-cases/DeleteNodeUseCase.ts | 2 +- .../application/use-cases/MoveNodeUseCase.ts | 2 +- .../use-cases/RemoveConnectionUseCase.ts | 2 +- .../use-cases/UpdateNodeDataUseCase.ts | 2 +- .../use-cases/ValidateTreeUseCase.ts | 0 .../editor}/application/use-cases/index.ts | 0 .../editor}/compiler/BehaviorTreeCompiler.tsx | 2 +- .../editor}/components/BehaviorTreeEditor.tsx | 2 +- .../components/blackboard/BlackboardPanel.tsx | 0 .../components/canvas/BehaviorTreeCanvas.tsx | 0 .../components/canvas/GridBackground.tsx | 0 .../src/editor}/components/canvas/index.ts | 0 .../components/common/DraggablePanel.tsx | 0 .../connections/ConnectionLayer.tsx | 0 .../connections/ConnectionRenderer.tsx | 0 .../editor}/components/connections/index.ts | 0 .../components/menu/NodeContextMenu.tsx | 0 .../components/menu/QuickCreateMenu.tsx | 2 +- .../components/nodes/BehaviorTreeNode.tsx | 2 +- .../nodes/BehaviorTreeNodeRenderer.tsx | 0 .../src/editor}/components/nodes/index.ts | 0 .../panels/BehaviorTreeEditorPanel.css | 0 .../panels/BehaviorTreeEditorPanel.tsx | 40 +- .../panels/BehaviorTreePropertiesPanel.css | 0 .../components/toolbar/EditorToolbar.tsx | 0 .../src/editor}/config/editorConstants.ts | 2 +- .../src/editor}/constants/index.ts | 0 .../src/editor}/domain/constants/RootNode.ts | 2 +- .../src/editor}/domain/errors/DomainError.ts | 0 .../domain/errors/NodeNotFoundError.ts | 0 .../editor}/domain/errors/ValidationError.ts | 0 .../src/editor}/domain/errors/index.ts | 0 .../src/editor}/domain/index.ts | 0 .../editor}/domain/interfaces/INodeFactory.ts | 2 +- .../editor}/domain/interfaces/IRepository.ts | 0 .../editor}/domain/interfaces/ISerializer.ts | 0 .../editor}/domain/interfaces/IValidator.ts | 0 .../src/editor}/domain/interfaces/index.ts | 0 .../src/editor}/domain/models/BehaviorTree.ts | 0 .../src/editor}/domain/models/Blackboard.ts | 0 .../src/editor}/domain/models/Connection.ts | 0 .../src/editor}/domain/models/Node.ts | 2 +- .../src/editor}/domain/models/index.ts | 0 .../editor}/domain/services/TreeValidator.ts | 0 .../src/editor}/domain/services/index.ts | 0 .../editor}/domain/value-objects/NodeType.ts | 0 .../editor}/domain/value-objects/Position.ts | 0 .../src/editor}/domain/value-objects/Size.ts | 0 .../src/editor}/domain/value-objects/index.ts | 0 .../GlobalBlackboardTypeGenerator.ts | 40 +- .../LocalBlackboardTypeGenerator.ts | 0 .../src/editor}/hooks/index.ts | 0 .../src/editor}/hooks/useCanvasInteraction.ts | 0 .../src/editor}/hooks/useCanvasMouseEvents.ts | 0 .../src/editor}/hooks/useCommandHistory.ts | 0 .../editor}/hooks/useConnectionOperations.ts | 0 .../src/editor}/hooks/useContextMenu.ts | 0 .../src/editor}/hooks/useDropHandler.ts | 2 +- .../src/editor}/hooks/useEditorHandlers.ts | 0 .../src/editor}/hooks/useEditorState.ts | 0 .../editor}/hooks/useExecutionController.ts | 0 .../src/editor}/hooks/useKeyboardShortcuts.ts | 0 .../src/editor}/hooks/useNodeDrag.ts | 0 .../src/editor}/hooks/useNodeOperations.ts | 2 +- .../src/editor}/hooks/useNodeTracking.ts | 0 .../src/editor}/hooks/usePortConnection.ts | 2 +- .../src/editor}/hooks/useQuickCreateMenu.ts | 2 +- packages/behavior-tree/src/editor/index.ts | 287 +++ .../infrastructure/events/EditorEventBus.ts | 0 .../infrastructure/factories/NodeFactory.ts | 2 +- .../editor}/infrastructure/factories/index.ts | 0 .../src/editor}/infrastructure/index.ts | 0 .../serialization/BehaviorTreeSerializer.ts | 2 +- .../infrastructure/serialization/index.ts | 0 .../services/NodeRegistryService.ts | 2 +- .../validation/BehaviorTreeValidator.ts | 0 .../editor}/interfaces/IEditorExtensions.ts | 2 +- .../BehaviorTreeNodeInspectorProvider.tsx | 8 +- .../editor}/services/BehaviorTreeService.ts | 0 .../src/editor}/services/FileSystemService.ts | 0 .../editor}/services/NotificationService.ts | 24 +- .../src/editor}/services/index.ts | 0 .../src/editor}/stores/ExecutionStatsStore.ts | 0 .../src/editor}/stores/index.ts | 0 .../src/editor}/stores/useUIStore.ts | 0 .../src/editor}/styles/BehaviorTreeNode.css | 0 .../src/editor}/styles/Toast.css | 0 .../src/editor}/types/Breakpoint.ts | 0 .../src/editor}/types/index.ts | 0 .../src/editor}/utils/BehaviorTreeExecutor.ts | 2 +- .../src/editor}/utils/DOMCache.ts | 0 .../src/editor}/utils/RuntimeLoader.ts | 2 +- .../src/editor}/utils/portUtils.ts | 0 .../BehaviorTreeAssetManager.ts | 0 .../BehaviorTreeData.ts | 0 .../BehaviorTreeExecutionSystem.ts | 17 +- .../BehaviorTreeRuntimeComponent.ts | 4 +- .../Executors/AlwaysFailExecutor.ts | 0 .../Executors/AlwaysSucceedExecutor.ts | 0 .../Executors/BlackboardCompare.ts | 0 .../Executors/BlackboardExists.ts | 0 .../Executors/ConditionalExecutor.ts | 0 .../Executors/CooldownExecutor.ts | 0 .../Executors/ExecuteAction.ts | 0 .../Executors/ExecuteCondition.ts | 0 .../Executors/InverterExecutor.ts | 0 .../Executors/LogAction.ts | 0 .../Executors/ModifyBlackboardValue.ts | 0 .../Executors/ParallelExecutor.ts | 0 .../Executors/ParallelSelectorExecutor.ts | 0 .../Executors/RandomProbability.ts | 0 .../Executors/RandomSelectorExecutor.ts | 0 .../Executors/RandomSequenceExecutor.ts | 0 .../Executors/RepeaterExecutor.ts | 0 .../Executors/SelectorExecutor.ts | 0 .../Executors/SequenceExecutor.ts | 0 .../Executors/ServiceDecorator.ts | 0 .../Executors/SetBlackboardValue.ts | 0 .../Executors/SubTreeExecutor.ts | 0 .../Executors/TimeoutExecutor.ts | 0 .../Executors/UntilFailExecutor.ts | 0 .../Executors/UntilSuccessExecutor.ts | 0 .../Executors/WaitAction.ts | 0 .../Executors/WaitActionExecutor.ts | 0 .../{Runtime => execution}/Executors/index.ts | 0 .../{Runtime => execution}/NodeExecutor.ts | 0 .../{Runtime => execution}/NodeMetadata.ts | 0 .../src/{Runtime => execution}/index.ts | 0 packages/behavior-tree/src/index.ts | 26 +- packages/behavior-tree/src/runtime.ts | 36 + packages/behavior-tree/tsconfig.json | 35 +- packages/behavior-tree/vite.config.ts | 83 +- packages/components/package.json | 6 +- packages/components/plugin.json | 20 + packages/components/src/CorePlugin.ts | 130 ++ packages/components/src/index.ts | 6 +- packages/components/tsconfig.json | 3 +- .../src/ECS/Decorators/PropertyDecorator.ts | 4 +- packages/core/src/ECS/Systems/EntitySystem.ts | 5 + packages/core/src/ECS/Utils/Matcher.ts | 51 +- .../tests/ECS/Systems/EntitySystem.test.ts | 354 +++ packages/core/tests/ECS/Utils/Matcher.test.ts | 53 + packages/ecs-engine-bindgen/package.json | 2 +- .../src/core/EngineBridge.ts | 115 + packages/ecs-engine-bindgen/src/index.ts | 2 +- .../src/systems/EngineRenderSystem.ts | 331 ++- .../src/wasm/es_engine.d.ts | 9 + packages/editor-app/index.html | 2 +- packages/editor-app/package.json | 5 +- .../editor-app/public/runtime.config.json | 2 +- packages/editor-app/runtime.config.json | 2 +- packages/editor-app/src-tauri/Cargo.toml | 2 +- packages/editor-app/src-tauri/src/main.rs | 2 +- packages/editor-app/src-tauri/tauri.conf.json | 6 +- packages/editor-app/src/App.tsx | 154 +- packages/editor-app/src/api/tauri.ts | 12 + .../src/app/managers/DialogManager.ts | 5 - .../src/app/managers/PluginInstaller.ts | 79 +- .../src/app/managers/ServiceRegistry.ts | 65 +- .../editor-app/src/components/AboutDialog.tsx | 13 +- .../src/components/AssetBrowser.tsx | 29 +- .../src/components/CompilerConfigDialog.tsx | 3 - .../src/components/EntityInspector.tsx | 3 - .../editor-app/src/components/FileTree.tsx | 21 +- .../components/FlexLayoutDockContainer.tsx | 222 +- .../editor-app/src/components/GitHubAuth.tsx | 7 - .../editor-app/src/components/MenuBar.tsx | 28 +- .../src/components/MiniParticleLogo.tsx | 178 ++ .../src/components/PluginListSetting.tsx | 198 ++ .../src/components/PluginManagerWindow.tsx | 438 ---- .../src/components/PluginMarketPanel.tsx | 440 ---- .../editor-app/src/components/PluginPanel.tsx | 256 --- .../src/components/PluginPublishWizard.tsx | 948 -------- .../src/components/PluginUpdateDialog.tsx | 354 --- .../src/components/PropertyInspector.tsx | 286 +-- .../src/components/SceneHierarchy.tsx | 137 +- .../src/components/SettingsWindow.tsx | 96 +- .../editor-app/src/components/StartupPage.tsx | 4 +- .../src/components/UserDashboard.tsx | 1095 --------- .../editor-app/src/components/UserProfile.tsx | 156 -- .../inspectors/fields/AssetField.css | 10 +- .../inspectors/views/EntityInspector.tsx | 3 - packages/editor-app/src/hooks/useEngine.ts | 27 +- packages/editor-app/src/locales/en.ts | 2 +- packages/editor-app/src/locales/zh.ts | 2 +- packages/editor-app/src/main.tsx | 1 + .../editor-app/src/plugins/GizmoPlugin.ts | 40 - .../src/plugins/SceneInspectorPlugin.ts | 121 - .../{ => builtin}/EditorAppearancePlugin.tsx | 60 +- .../src/plugins/builtin/GizmoPlugin.ts | 50 + .../plugins/builtin/PluginConfigPlugin.tsx | 77 + .../plugins/{ => builtin}/ProfilerPlugin.tsx | 101 +- .../plugins/builtin/ProjectSettingsPlugin.tsx | 173 ++ .../plugins/builtin/SceneInspectorPlugin.ts | 199 ++ .../editor-app/src/plugins/builtin/index.ts | 11 + .../editor-app/src/services/EngineService.ts | 247 +- .../src/services/ImportMapManager.ts | 73 - .../editor-app/src/services/PluginLoader.ts | 281 ++- .../src/services/PluginMarketService.ts | 312 --- .../src/services/PluginPublishService.ts | 622 ----- .../src/services/PluginSDKRegistry.ts | 159 ++ .../src/services/ProfilerService.ts | 6 +- .../src/services/RuntimeResolver.ts | 71 +- .../editor-app/src/styles/AboutDialog.css | 166 +- packages/editor-app/src/styles/App.css | 6 +- .../editor-app/src/styles/AssetBrowser.css | 2 +- .../src/styles/AssetPickerDialog.css | 2 +- .../editor-app/src/styles/CompileDialog.css | 2 +- .../src/styles/CompilerConfigDialog.css | 2 +- .../editor-app/src/styles/ConfirmDialog.css | 2 +- .../editor-app/src/styles/ConsolePanel.css | 2 +- .../editor-app/src/styles/ContextMenu.css | 2 +- .../editor-app/src/styles/EntityInspector.css | 4 +- .../editor-app/src/styles/ErrorDialog.css | 2 +- .../src/styles/ExportRuntimeDialog.css | 2 +- .../editor-app/src/styles/FlexLayoutDock.css | 12 + packages/editor-app/src/styles/GameView.css | 6 +- .../src/styles/GitHubLoginDialog.css | 2 +- packages/editor-app/src/styles/JsonViewer.css | 2 +- packages/editor-app/src/styles/MenuBar.css | 2 +- .../src/styles/PluginListSetting.css | 169 ++ .../src/styles/PluginManagerWindow.css | 2 +- .../src/styles/PluginPublishWizard.css | 2 +- .../src/styles/PluginUpdateDialog.css | 2 +- .../editor-app/src/styles/ProfilerWindow.css | 2 +- .../src/styles/ProjectCreationWizard.css | 2 +- .../editor-app/src/styles/PromptDialog.css | 2 +- .../src/styles/PropertyInspector.css | 4 +- .../editor-app/src/styles/QRCodeDialog.css | 2 +- .../editor-app/src/styles/SceneHierarchy.css | 2 +- .../editor-app/src/styles/SettingsWindow.css | 2 +- .../editor-app/src/styles/StartupLogo.css | 2 +- .../editor-app/src/styles/StartupPage.css | 4 +- packages/editor-app/src/styles/Toast.css | 2 +- .../editor-app/src/styles/UserDashboard.css | 4 +- .../editor-app/src/styles/UserProfile.css | 2 +- packages/editor-app/src/styles/Viewport.css | 6 +- .../editor-app/src/styles/design-tokens.css | 31 +- .../editor-core/src/Plugin/IPluginLoader.ts | 355 +++ .../src/Plugin/PluginDescriptor.ts | 163 ++ .../editor-core/src/Plugin/PluginManager.ts | 775 +++++++ packages/editor-core/src/Plugin/index.ts | 8 + .../src/Plugins/EditorPluginManager.ts | 479 ---- .../editor-core/src/Plugins/IEditorPlugin.ts | 286 --- .../editor-core/src/Plugins/PluginRegistry.ts | 528 ----- .../editor-core/src/Plugins/PluginTypes.ts | 227 -- .../src/Services/ComponentActionRegistry.ts | 15 +- .../src/Services/FileActionRegistry.ts | 5 +- .../src/Services/ProjectService.ts | 107 +- .../src/Services/SceneManagerService.ts | 30 +- .../src/Services/SceneTemplateRegistry.ts | 128 ++ .../src/Services/SerializerRegistry.ts | 2 +- .../src/Services/SettingsRegistry.ts | 2 +- packages/editor-core/src/Types/UITypes.ts | 104 +- packages/editor-core/src/index.ts | 11 +- packages/editor-runtime/package.json | 15 +- packages/editor-runtime/src/PluginAPI.ts | 109 + packages/editor-runtime/src/index.ts | 127 +- packages/editor-runtime/tsconfig.json | 6 +- packages/editor-runtime/vite.config.ts | 20 +- packages/engine/package.json | 11 + packages/engine/src/core/engine.rs | 31 +- packages/engine/src/lib.rs | 12 + packages/engine/src/renderer/gizmo.rs | 215 ++ packages/platform-web/package.json | 22 +- packages/platform-web/rollup.config.js | 12 +- .../platform-web/rollup.runtime.config.js | 25 +- packages/platform-web/src/RuntimeSystems.ts | 364 +++ packages/platform-web/src/index.ts | 9 + packages/platform-web/src/runtime.ts | 82 +- packages/platform-web/tsconfig.json | 2 +- packages/tilemap-editor/package.json | 51 - .../src/TilemapEditorPluginDefinition.ts | 123 - packages/tilemap-editor/src/index.ts | 30 - packages/tilemap-editor/tsconfig.json | 45 - packages/tilemap/package.json | 63 +- packages/tilemap/plugin.json | 41 + packages/tilemap/src/TilemapRuntimeModule.ts | 32 + packages/tilemap/src/editor/TilemapPlugin.ts | 89 + .../src/editor}/components/TilemapCanvas.tsx | 2 +- .../src/editor}/components/TilesetPreview.tsx | 0 .../editor}/components/panels/LayerPanel.tsx | 2 +- .../components/panels/TilemapEditorPanel.tsx | 2 +- .../components/panels/TilesetPanel.tsx | 2 +- .../src/editor}/gizmos/TilemapGizmo.ts | 2 +- .../src/editor/index.ts} | 275 +-- .../providers/TilemapInspectorProvider.tsx | 2 +- .../src/editor}/stores/TilemapEditorStore.ts | 0 .../src/editor}/styles/TilemapEditor.css | 0 .../src/editor}/tools/BrushTool.ts | 0 .../src/editor}/tools/EraserTool.ts | 0 .../src/editor}/tools/FillTool.ts | 0 .../src/editor}/tools/ITilemapTool.ts | 2 +- packages/tilemap/src/index.ts | 22 +- packages/tilemap/src/runtime.ts | 31 + packages/tilemap/tsconfig.json | 11 +- packages/tilemap/vite.config.ts | 100 + packages/ui-editor/package.json | 49 - packages/ui-editor/src/index.ts | 6 - packages/ui-editor/tsconfig.json | 26 - packages/ui/package.json | 40 +- packages/ui/plugin.json | 50 + packages/ui/src/UIRuntimeModule.ts | 91 + .../ui/src/components/UITransformComponent.ts | 8 +- packages/ui/src/editor/UIPlugin.ts | 159 ++ .../src/editor}/gizmos/UITransformGizmo.ts | 29 +- .../src => ui/src/editor}/gizmos/index.ts | 0 .../src/editor/index.ts} | 209 +- .../inspectors/UITransformInspector.tsx | 4 +- .../src => ui/src/editor}/inspectors/index.ts | 0 packages/ui/src/index.ts | 66 +- packages/ui/src/runtime.ts | 111 + packages/ui/src/systems/UILayoutSystem.ts | 148 +- .../ui/src/systems/UIRenderDataProvider.ts | 491 +--- packages/ui/src/systems/index.ts | 3 + .../systems/render/UIButtonRenderSystem.ts | 159 ++ .../render/UIProgressBarRenderSystem.ts | 350 +++ .../src/systems/render/UIRectRenderSystem.ts | 193 ++ .../src/systems/render/UIRenderBeginSystem.ts | 53 + .../src/systems/render/UIRenderCollector.ts | 362 +++ .../render/UIScrollViewRenderSystem.ts | 194 ++ .../systems/render/UISliderRenderSystem.ts | 277 +++ .../src/systems/render/UITextRenderSystem.ts | 322 +++ packages/ui/src/systems/render/index.ts | 33 + packages/ui/tsconfig.json | 15 +- packages/ui/vite.config.ts | 19 +- pnpm-lock.yaml | 1187 ++-------- thirdparty/admin-backend | 1 - thirdparty/cocos-nexus | 1 - 367 files changed, 10661 insertions(+), 12473 deletions(-) delete mode 100644 packages/behavior-tree-editor/package.json delete mode 100644 packages/behavior-tree-editor/pnpm-lock.yaml delete mode 100644 packages/behavior-tree-editor/rollup.config.cjs delete mode 100644 packages/behavior-tree-editor/scripts/copy-css.js delete mode 100644 packages/behavior-tree-editor/src/BehaviorTreeModule.ts delete mode 100644 packages/behavior-tree-editor/src/BehaviorTreePlugin.ts delete mode 100644 packages/behavior-tree-editor/src/index.ts delete mode 100644 packages/behavior-tree-editor/tsconfig.json create mode 100644 packages/behavior-tree/plugin.json delete mode 100644 packages/behavior-tree/src/BehaviorTreePlugin.ts create mode 100644 packages/behavior-tree/src/BehaviorTreeRuntimeModule.ts create mode 100644 packages/behavior-tree/src/editor/BehaviorTreePlugin.ts rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/PluginContext.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/commands/CommandManager.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/commands/ICommand.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/commands/ITreeState.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/commands/tree/AddConnectionCommand.ts (94%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/commands/tree/CreateNodeCommand.ts (93%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/commands/tree/DeleteNodeCommand.ts (94%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/commands/tree/MoveNodeCommand.ts (96%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/commands/tree/RemoveConnectionCommand.ts (96%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/commands/tree/UpdateNodeDataCommand.ts (94%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/commands/tree/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/interfaces/IExecutionHooks.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/services/BlackboardManager.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/services/ExecutionController.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/services/GlobalBlackboardService.ts (99%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/state/BehaviorTreeDataStore.ts (99%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/use-cases/AddConnectionUseCase.ts (95%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/use-cases/CreateNodeUseCase.ts (92%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/use-cases/DeleteNodeUseCase.ts (96%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/use-cases/MoveNodeUseCase.ts (94%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/use-cases/RemoveConnectionUseCase.ts (92%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/use-cases/UpdateNodeDataUseCase.ts (90%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/use-cases/ValidateTreeUseCase.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/application/use-cases/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/compiler/BehaviorTreeCompiler.tsx (99%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/BehaviorTreeEditor.tsx (99%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/blackboard/BlackboardPanel.tsx (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/canvas/BehaviorTreeCanvas.tsx (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/canvas/GridBackground.tsx (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/canvas/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/common/DraggablePanel.tsx (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/connections/ConnectionLayer.tsx (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/connections/ConnectionRenderer.tsx (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/connections/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/menu/NodeContextMenu.tsx (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/menu/QuickCreateMenu.tsx (99%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/nodes/BehaviorTreeNode.tsx (99%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/nodes/BehaviorTreeNodeRenderer.tsx (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/nodes/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/panels/BehaviorTreeEditorPanel.css (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/panels/BehaviorTreeEditorPanel.tsx (91%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/panels/BehaviorTreePropertiesPanel.css (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/components/toolbar/EditorToolbar.tsx (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/config/editorConstants.ts (94%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/constants/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/constants/RootNode.ts (92%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/errors/DomainError.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/errors/NodeNotFoundError.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/errors/ValidationError.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/errors/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/interfaces/INodeFactory.ts (91%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/interfaces/IRepository.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/interfaces/ISerializer.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/interfaces/IValidator.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/interfaces/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/models/BehaviorTree.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/models/Blackboard.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/models/Connection.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/models/Node.ts (98%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/models/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/services/TreeValidator.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/services/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/value-objects/NodeType.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/value-objects/Position.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/value-objects/Size.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/domain/value-objects/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/generators/GlobalBlackboardTypeGenerator.ts (91%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/generators/LocalBlackboardTypeGenerator.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useCanvasInteraction.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useCanvasMouseEvents.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useCommandHistory.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useConnectionOperations.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useContextMenu.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useDropHandler.ts (98%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useEditorHandlers.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useEditorState.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useExecutionController.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useKeyboardShortcuts.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useNodeDrag.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useNodeOperations.ts (98%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useNodeTracking.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/usePortConnection.ts (99%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/hooks/useQuickCreateMenu.ts (99%) create mode 100644 packages/behavior-tree/src/editor/index.ts rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/infrastructure/events/EditorEventBus.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/infrastructure/factories/NodeFactory.ts (97%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/infrastructure/factories/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/infrastructure/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/infrastructure/serialization/BehaviorTreeSerializer.ts (99%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/infrastructure/serialization/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/infrastructure/services/NodeRegistryService.ts (99%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/infrastructure/validation/BehaviorTreeValidator.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/interfaces/IEditorExtensions.ts (99%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/providers/BehaviorTreeNodeInspectorProvider.tsx (98%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/services/BehaviorTreeService.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/services/FileSystemService.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/services/NotificationService.ts (67%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/services/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/stores/ExecutionStatsStore.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/stores/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/stores/useUIStore.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/styles/BehaviorTreeNode.css (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/styles/Toast.css (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/types/Breakpoint.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/types/index.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/utils/BehaviorTreeExecutor.ts (99%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/utils/DOMCache.ts (100%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/utils/RuntimeLoader.ts (98%) rename packages/{behavior-tree-editor/src => behavior-tree/src/editor}/utils/portUtils.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/BehaviorTreeAssetManager.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/BehaviorTreeData.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/BehaviorTreeExecutionSystem.ts (92%) rename packages/behavior-tree/src/{Runtime => execution}/BehaviorTreeRuntimeComponent.ts (96%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/AlwaysFailExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/AlwaysSucceedExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/BlackboardCompare.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/BlackboardExists.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/ConditionalExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/CooldownExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/ExecuteAction.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/ExecuteCondition.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/InverterExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/LogAction.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/ModifyBlackboardValue.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/ParallelExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/ParallelSelectorExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/RandomProbability.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/RandomSelectorExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/RandomSequenceExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/RepeaterExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/SelectorExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/SequenceExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/ServiceDecorator.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/SetBlackboardValue.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/SubTreeExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/TimeoutExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/UntilFailExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/UntilSuccessExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/WaitAction.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/WaitActionExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/Executors/index.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/NodeExecutor.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/NodeMetadata.ts (100%) rename packages/behavior-tree/src/{Runtime => execution}/index.ts (100%) create mode 100644 packages/behavior-tree/src/runtime.ts create mode 100644 packages/components/plugin.json create mode 100644 packages/components/src/CorePlugin.ts create mode 100644 packages/editor-app/src/components/MiniParticleLogo.tsx create mode 100644 packages/editor-app/src/components/PluginListSetting.tsx delete mode 100644 packages/editor-app/src/components/PluginManagerWindow.tsx delete mode 100644 packages/editor-app/src/components/PluginMarketPanel.tsx delete mode 100644 packages/editor-app/src/components/PluginPanel.tsx delete mode 100644 packages/editor-app/src/components/PluginPublishWizard.tsx delete mode 100644 packages/editor-app/src/components/PluginUpdateDialog.tsx delete mode 100644 packages/editor-app/src/components/UserDashboard.tsx delete mode 100644 packages/editor-app/src/components/UserProfile.tsx delete mode 100644 packages/editor-app/src/plugins/GizmoPlugin.ts delete mode 100644 packages/editor-app/src/plugins/SceneInspectorPlugin.ts rename packages/editor-app/src/plugins/{ => builtin}/EditorAppearancePlugin.tsx (74%) create mode 100644 packages/editor-app/src/plugins/builtin/GizmoPlugin.ts create mode 100644 packages/editor-app/src/plugins/builtin/PluginConfigPlugin.tsx rename packages/editor-app/src/plugins/{ => builtin}/ProfilerPlugin.tsx (72%) create mode 100644 packages/editor-app/src/plugins/builtin/ProjectSettingsPlugin.tsx create mode 100644 packages/editor-app/src/plugins/builtin/SceneInspectorPlugin.ts create mode 100644 packages/editor-app/src/plugins/builtin/index.ts delete mode 100644 packages/editor-app/src/services/ImportMapManager.ts delete mode 100644 packages/editor-app/src/services/PluginMarketService.ts delete mode 100644 packages/editor-app/src/services/PluginPublishService.ts create mode 100644 packages/editor-app/src/services/PluginSDKRegistry.ts create mode 100644 packages/editor-app/src/styles/PluginListSetting.css create mode 100644 packages/editor-core/src/Plugin/IPluginLoader.ts create mode 100644 packages/editor-core/src/Plugin/PluginDescriptor.ts create mode 100644 packages/editor-core/src/Plugin/PluginManager.ts create mode 100644 packages/editor-core/src/Plugin/index.ts delete mode 100644 packages/editor-core/src/Plugins/EditorPluginManager.ts delete mode 100644 packages/editor-core/src/Plugins/IEditorPlugin.ts delete mode 100644 packages/editor-core/src/Plugins/PluginRegistry.ts delete mode 100644 packages/editor-core/src/Plugins/PluginTypes.ts create mode 100644 packages/editor-core/src/Services/SceneTemplateRegistry.ts create mode 100644 packages/editor-runtime/src/PluginAPI.ts create mode 100644 packages/platform-web/src/RuntimeSystems.ts delete mode 100644 packages/tilemap-editor/package.json delete mode 100644 packages/tilemap-editor/src/TilemapEditorPluginDefinition.ts delete mode 100644 packages/tilemap-editor/src/index.ts delete mode 100644 packages/tilemap-editor/tsconfig.json create mode 100644 packages/tilemap/plugin.json create mode 100644 packages/tilemap/src/TilemapRuntimeModule.ts create mode 100644 packages/tilemap/src/editor/TilemapPlugin.ts rename packages/{tilemap-editor/src => tilemap/src/editor}/components/TilemapCanvas.tsx (99%) rename packages/{tilemap-editor/src => tilemap/src/editor}/components/TilesetPreview.tsx (100%) rename packages/{tilemap-editor/src => tilemap/src/editor}/components/panels/LayerPanel.tsx (99%) rename packages/{tilemap-editor/src => tilemap/src/editor}/components/panels/TilemapEditorPanel.tsx (99%) rename packages/{tilemap-editor/src => tilemap/src/editor}/components/panels/TilesetPanel.tsx (98%) rename packages/{tilemap-editor/src => tilemap/src/editor}/gizmos/TilemapGizmo.ts (98%) rename packages/{tilemap-editor/src/TilemapEditorPlugin.ts => tilemap/src/editor/index.ts} (68%) rename packages/{tilemap-editor/src => tilemap/src/editor}/providers/TilemapInspectorProvider.tsx (98%) rename packages/{tilemap-editor/src => tilemap/src/editor}/stores/TilemapEditorStore.ts (100%) rename packages/{tilemap-editor/src => tilemap/src/editor}/styles/TilemapEditor.css (100%) rename packages/{tilemap-editor/src => tilemap/src/editor}/tools/BrushTool.ts (100%) rename packages/{tilemap-editor/src => tilemap/src/editor}/tools/EraserTool.ts (100%) rename packages/{tilemap-editor/src => tilemap/src/editor}/tools/FillTool.ts (100%) rename packages/{tilemap-editor/src => tilemap/src/editor}/tools/ITilemapTool.ts (93%) create mode 100644 packages/tilemap/src/runtime.ts create mode 100644 packages/tilemap/vite.config.ts delete mode 100644 packages/ui-editor/package.json delete mode 100644 packages/ui-editor/src/index.ts delete mode 100644 packages/ui-editor/tsconfig.json create mode 100644 packages/ui/plugin.json create mode 100644 packages/ui/src/UIRuntimeModule.ts create mode 100644 packages/ui/src/editor/UIPlugin.ts rename packages/{ui-editor/src => ui/src/editor}/gizmos/UITransformGizmo.ts (51%) rename packages/{ui-editor/src => ui/src/editor}/gizmos/index.ts (100%) rename packages/{ui-editor/src/UIEditorPlugin.ts => ui/src/editor/index.ts} (68%) rename packages/{ui-editor/src => ui/src/editor}/inspectors/UITransformInspector.tsx (99%) rename packages/{ui-editor/src => ui/src/editor}/inspectors/index.ts (100%) create mode 100644 packages/ui/src/runtime.ts create mode 100644 packages/ui/src/systems/render/UIButtonRenderSystem.ts create mode 100644 packages/ui/src/systems/render/UIProgressBarRenderSystem.ts create mode 100644 packages/ui/src/systems/render/UIRectRenderSystem.ts create mode 100644 packages/ui/src/systems/render/UIRenderBeginSystem.ts create mode 100644 packages/ui/src/systems/render/UIRenderCollector.ts create mode 100644 packages/ui/src/systems/render/UIScrollViewRenderSystem.ts create mode 100644 packages/ui/src/systems/render/UISliderRenderSystem.ts create mode 100644 packages/ui/src/systems/render/UITextRenderSystem.ts create mode 100644 packages/ui/src/systems/render/index.ts delete mode 160000 thirdparty/admin-backend delete mode 160000 thirdparty/cocos-nexus diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aefd0962..f3058715 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,11 @@ jobs: cd packages/platform-common && pnpm run build cd ../asset-system && pnpm run build cd ../components && pnpm run build + cd ../editor-core && pnpm run build + cd ../ui && pnpm run build + cd ../editor-runtime && pnpm run build + cd ../behavior-tree && pnpm run build + cd ../tilemap && pnpm run build - name: Build ecs-engine-bindgen run: | diff --git a/.github/workflows/release-editor.yml b/.github/workflows/release-editor.yml index 1b93325d..ac6a82c5 100644 --- a/.github/workflows/release-editor.yml +++ b/.github/workflows/release-editor.yml @@ -96,16 +96,6 @@ jobs: cd packages/components pnpm run build - - name: Build behavior-tree package - run: | - cd packages/behavior-tree - pnpm run build - - - name: Build UI package - run: | - cd packages/ui - pnpm run build - # ===== 第三层:Rust WASM 引擎 ===== - name: Install wasm-pack run: cargo install wasm-pack @@ -129,34 +119,35 @@ jobs: cd packages/ecs-engine-bindgen pnpm run build - # ===== 第四层:依赖 asset-system 的包 ===== + # ===== 第四层:依赖 ecs-engine-bindgen/asset-system 的包 ===== - name: Build editor-core package run: | cd packages/editor-core pnpm run build + # ===== 第五层:依赖 editor-core 的包 ===== + - name: Build UI package + run: | + cd packages/ui + pnpm run build + - name: Build tilemap package run: | cd packages/tilemap pnpm run build - # ===== 第五层:依赖 editor-core 的包 ===== - name: Build editor-runtime package run: | cd packages/editor-runtime pnpm run build - - name: Build UI editor package + # ===== 第六层:依赖 editor-runtime 的包 ===== + - name: Build behavior-tree package run: | - cd packages/ui-editor + cd packages/behavior-tree pnpm run build - - name: Build tilemap-editor package - run: | - cd packages/tilemap-editor - pnpm run build - - # ===== 第六层:平台包 ===== + # ===== 第七层:平台包(依赖 ui, tilemap) ===== - name: Build platform-web package run: | cd packages/platform-web diff --git a/docs/guide/service-container.md b/docs/guide/service-container.md index 2944a5c0..e5f1086e 100644 --- a/docs/guide/service-container.md +++ b/docs/guide/service-container.md @@ -33,6 +33,26 @@ class MyService implements IService { } ``` +#### 服务标识符(ServiceIdentifier) + +服务标识符用于在容器中唯一标识一个服务,支持两种类型: + +- **类构造函数**: 直接使用服务类作为标识符,适用于具体实现类 +- **Symbol**: 使用 Symbol 作为标识符,适用于接口抽象(推荐用于插件和跨包场景) + +```typescript +// 方式1: 使用类作为标识符 +Core.services.registerSingleton(DataService); +const data = Core.services.resolve(DataService); + +// 方式2: 使用 Symbol 作为标识符(推荐用于接口) +const IFileSystem = Symbol.for('IFileSystem'); +Core.services.registerInstance(IFileSystem, new TauriFileSystem()); +const fs = Core.services.resolve(IFileSystem); +``` + +> **提示**: 使用 `Symbol.for()` 而非 `Symbol()` 可确保跨包/跨模块共享同一个标识符。详见[高级用法 - 接口与 Symbol 标识符模式](#接口与-symbol-标识符模式)。 + #### 生命周期 服务容器支持两种生命周期: @@ -333,21 +353,20 @@ class GameService implements IService { } ``` -### @Inject 装饰器 +### @InjectProperty 装饰器 -在构造函数中注入依赖: +通过属性装饰器注入依赖。注入时机是在构造函数执行后、`onInitialize()` 调用前完成: ```typescript -import { Injectable, Inject, IService } from '@esengine/ecs-framework'; +import { Injectable, InjectProperty, IService } from '@esengine/ecs-framework'; @Injectable() class PlayerService implements IService { - constructor( - @Inject(DataService) private data: DataService, - @Inject(GameService) private game: GameService - ) { - // data 和 game 会自动从容器中解析 - } + @InjectProperty(DataService) + private data!: DataService; + + @InjectProperty(GameService) + private game!: GameService; dispose(): void { // 清理资源 @@ -355,6 +374,35 @@ class PlayerService implements IService { } ``` +在 EntitySystem 中使用属性注入: + +```typescript +@Injectable() +class CombatSystem extends EntitySystem { + @InjectProperty(TimeService) + private timeService!: TimeService; + + @InjectProperty(AudioService) + private audio!: AudioService; + + constructor() { + super(Matcher.all(Health, Attack)); + } + + onInitialize(): void { + // 此时属性已注入完成,可以安全使用 + console.log('Delta time:', this.timeService.getDeltaTime()); + } + + processEntity(entity: Entity): void { + // 使用注入的服务 + this.audio.playSound('attack'); + } +} +``` + +> **注意**: 属性声明时使用 `!` 断言(如 `private data!: DataService`),表示该属性会在使用前被注入。 + ### 注册可注入服务 使用 `registerInjectable` 自动处理依赖注入: @@ -362,10 +410,10 @@ class PlayerService implements IService { ```typescript import { registerInjectable } from '@esengine/ecs-framework'; -// 注册服务(会自动解析@Inject依赖) +// 注册服务(会自动解析 @InjectProperty 依赖) registerInjectable(Core.services, PlayerService); -// 解析时会自动注入依赖 +// 解析时会自动注入属性依赖 const player = Core.services.resolve(PlayerService); ``` @@ -493,22 +541,164 @@ registerInjectable(Core.services, NetworkService); ## 高级用法 -### 服务替换(测试) +### 接口与 Symbol 标识符模式 -在测试中替换真实服务为模拟服务: +在大型项目或需要跨平台适配的游戏中,推荐使用"接口 + Symbol.for 标识符"模式。这种模式实现了真正的依赖倒置,让代码依赖于抽象而非具体实现。 + +#### 为什么使用 Symbol.for + +- **跨包共享**: `Symbol.for('key')` 在全局 Symbol 注册表中创建/获取 Symbol,确保不同包中使用相同的标识符 +- **接口解耦**: 消费者只依赖接口定义,不依赖具体实现类 +- **可替换实现**: 可以在运行时注入不同的实现(如测试 Mock、不同平台适配) + +#### 定义接口和标识符 + +以音频服务为例,游戏需要在不同平台(Web、微信小游戏、原生App)使用不同的音频实现: ```typescript -// 测试代码 -class MockDataService implements IService { - getData(key: string) { - return 'mock data'; - } - - dispose(): void {} +// IAudioService.ts - 定义接口和标识符 +export interface IAudioService { + dispose(): void; + playSound(id: string): void; + playMusic(id: string, loop?: boolean): void; + stopMusic(): void; + setVolume(volume: number): void; + preload(id: string, url: string): Promise; } -// 注册模拟服务(用于测试) -Core.services.registerInstance(DataService, new MockDataService()); +// 使用 Symbol.for 确保跨包共享同一个 Symbol +export const IAudioService = Symbol.for('IAudioService'); +``` + +#### 实现接口 + +```typescript +// WebAudioService.ts - Web 平台实现 +import { IAudioService } from './IAudioService'; + +export class WebAudioService implements IAudioService { + private audioContext: AudioContext; + private sounds: Map = new Map(); + + constructor() { + this.audioContext = new AudioContext(); + } + + playSound(id: string): void { + const buffer = this.sounds.get(id); + if (buffer) { + const source = this.audioContext.createBufferSource(); + source.buffer = buffer; + source.connect(this.audioContext.destination); + source.start(); + } + } + + async preload(id: string, url: string): Promise { + const response = await fetch(url); + const arrayBuffer = await response.arrayBuffer(); + const audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer); + this.sounds.set(id, audioBuffer); + } + + // ... 其他方法实现 + + dispose(): void { + this.audioContext.close(); + this.sounds.clear(); + } +} +``` + +```typescript +// WechatAudioService.ts - 微信小游戏平台实现 +export class WechatAudioService implements IAudioService { + private innerAudioContexts: Map = new Map(); + + playSound(id: string): void { + const ctx = this.innerAudioContexts.get(id); + if (ctx) { + ctx.play(); + } + } + + async preload(id: string, url: string): Promise { + const ctx = wx.createInnerAudioContext(); + ctx.src = url; + this.innerAudioContexts.set(id, ctx); + } + + // ... 其他方法实现 + + dispose(): void { + for (const ctx of this.innerAudioContexts.values()) { + ctx.destroy(); + } + this.innerAudioContexts.clear(); + } +} +``` + +#### 注册和使用 + +```typescript +import { IAudioService } from './IAudioService'; +import { WebAudioService } from './WebAudioService'; +import { WechatAudioService } from './WechatAudioService'; + +// 根据平台注册不同实现 +if (typeof wx !== 'undefined') { + Core.services.registerInstance(IAudioService, new WechatAudioService()); +} else { + Core.services.registerInstance(IAudioService, new WebAudioService()); +} + +// 业务代码中使用 - 不关心具体实现 +const audio = Core.services.resolve(IAudioService); +await audio.preload('explosion', '/sounds/explosion.mp3'); +audio.playSound('explosion'); +``` + +#### 跨模块使用 + +```typescript +// 在游戏系统中使用 +import { IAudioService } from '@mygame/core'; + +class CombatSystem extends EntitySystem { + private audio: IAudioService; + + initialize(): void { + // 获取音频服务,不需要知道具体实现 + this.audio = this.scene.services.resolve(IAudioService); + } + + onEntityDeath(entity: Entity): void { + this.audio.playSound('death'); + } +} +``` + +#### Symbol vs Symbol.for + +```typescript +// Symbol() - 每次创建唯一的 Symbol +const sym1 = Symbol('test'); +const sym2 = Symbol('test'); +console.log(sym1 === sym2); // false - 不同的 Symbol + +// Symbol.for() - 在全局注册表中共享 +const sym3 = Symbol.for('test'); +const sym4 = Symbol.for('test'); +console.log(sym3 === sym4); // true - 同一个 Symbol + +// 跨包场景 +// package-a/index.ts +export const IMyService = Symbol.for('IMyService'); + +// package-b/index.ts (不同的包) +const IMyService = Symbol.for('IMyService'); +// 与 package-a 中的是同一个 Symbol! ``` ### 循环依赖检测 diff --git a/packages/behavior-tree-editor/package.json b/packages/behavior-tree-editor/package.json deleted file mode 100644 index 5d159971..00000000 --- a/packages/behavior-tree-editor/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "@esengine/behavior-tree-editor", - "version": "1.0.0", - "description": "Behavior Tree Editor Plugin for ECS Framework", - "type": "module", - "main": "dist/index.esm.js", - "module": "dist/index.esm.js", - "types": "dist/index.d.ts", - "scripts": { - "clean": "rimraf bin dist tsconfig.tsbuildinfo", - "prebuild": "npm run clean", - "build": "npm run build:tsc && npm run copy:css && npm run build:rollup", - "build:tsc": "tsc", - "copy:css": "node scripts/copy-css.js", - "build:rollup": "rollup -c", - "dev": "rollup -c -w" - }, - "keywords": [ - "ecs", - "behavior-tree", - "editor", - "plugin" - ], - "author": "", - "license": "MIT", - "devDependencies": { - "@esengine/behavior-tree": "workspace:*", - "@esengine/editor-runtime": "workspace:*", - "@rollup/plugin-commonjs": "^28.0.1", - "@rollup/plugin-node-resolve": "^15.3.0", - "@rollup/plugin-replace": "^6.0.3", - "@types/react": "^18.3.18", - "rimraf": "^6.0.1", - "rollup": "^4.28.1", - "rollup-plugin-copy": "^3.5.0", - "rollup-plugin-dts": "^6.1.1", - "rollup-plugin-postcss": "^4.0.2", - "typescript": "^5.8.2" - }, - "peerDependencies": { - "@esengine/behavior-tree": "*", - "@esengine/editor-runtime": "*" - }, - "dependencies": { - "mobx": "^6.15.0", - "mobx-react-lite": "^4.1.1" - } -} diff --git a/packages/behavior-tree-editor/pnpm-lock.yaml b/packages/behavior-tree-editor/pnpm-lock.yaml deleted file mode 100644 index 370cd040..00000000 --- a/packages/behavior-tree-editor/pnpm-lock.yaml +++ /dev/null @@ -1,2039 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - '@tauri-apps/api': - specifier: '*' - version: 2.9.0 - '@tauri-apps/plugin-dialog': - specifier: '*' - version: 2.4.2 - '@tauri-apps/plugin-http': - specifier: '*' - version: 2.5.4 - mobx: - specifier: ^6.15.0 - version: 6.15.0 - mobx-react-lite: - specifier: ^4.1.1 - version: 4.1.1(mobx@6.15.0)(react@18.3.1) - tsyringe: - specifier: '*' - version: 4.10.0 - devDependencies: - '@esengine/behavior-tree': - specifier: file:../behavior-tree - version: file:../behavior-tree(@esengine/ecs-framework@file:../core) - '@esengine/ecs-framework': - specifier: file:../core - version: file:../core - '@esengine/editor-core': - specifier: file:../editor-core - version: file:../editor-core(@esengine/ecs-framework@file:../core) - '@rollup/plugin-commonjs': - specifier: ^28.0.1 - version: 28.0.9(rollup@4.53.3) - '@rollup/plugin-node-resolve': - specifier: ^15.3.0 - version: 15.3.1(rollup@4.53.3) - '@types/react': - specifier: ^18.3.18 - version: 18.3.27 - lucide-react: - specifier: ^0.469.0 - version: 0.469.0(react@18.3.1) - react: - specifier: ^18.3.1 - version: 18.3.1 - rimraf: - specifier: ^6.0.1 - version: 6.1.2 - rollup: - specifier: ^4.28.1 - version: 4.53.3 - rollup-plugin-copy: - specifier: ^3.5.0 - version: 3.5.0 - rollup-plugin-dts: - specifier: ^6.1.1 - version: 6.2.3(rollup@4.53.3)(typescript@5.9.3) - rollup-plugin-postcss: - specifier: ^4.0.2 - version: 4.0.2(postcss@8.5.6) - typescript: - specifier: ^5.8.2 - version: 5.9.3 - zustand: - specifier: ^5.0.2 - version: 5.0.8(@types/react@18.3.27)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)) - -packages: - - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - - '@esengine/behavior-tree@file:../behavior-tree': - resolution: {directory: ../behavior-tree, type: directory} - peerDependencies: - '@esengine/ecs-framework': ^2.2.8 - - '@esengine/ecs-framework@file:../core': - resolution: {directory: ../core, type: directory} - - '@esengine/editor-core@file:../editor-core': - resolution: {directory: ../editor-core, type: directory} - peerDependencies: - '@esengine/ecs-framework': ^2.2.8 - - '@isaacs/balanced-match@4.0.1': - resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} - engines: {node: 20 || >=22} - - '@isaacs/brace-expansion@5.0.0': - resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} - engines: {node: 20 || >=22} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@rollup/plugin-commonjs@28.0.9': - resolution: {integrity: sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==} - engines: {node: '>=16.0.0 || 14 >= 14.17'} - peerDependencies: - rollup: ^2.68.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/plugin-node-resolve@15.3.1': - resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^2.78.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/pluginutils@5.3.0': - resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/rollup-android-arm-eabi@4.53.3': - resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.53.3': - resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.53.3': - resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.53.3': - resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.53.3': - resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.53.3': - resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.53.3': - resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.53.3': - resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.53.3': - resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-musl@4.53.3': - resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-loong64-gnu@4.53.3': - resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-ppc64-gnu@4.53.3': - resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.53.3': - resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-riscv64-musl@4.53.3': - resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.53.3': - resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} - cpu: [s390x] - os: [linux] - - '@rollup/rollup-linux-x64-gnu@4.53.3': - resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-linux-x64-musl@4.53.3': - resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-openharmony-arm64@4.53.3': - resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.53.3': - resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.53.3': - resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.53.3': - resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.53.3': - resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} - cpu: [x64] - os: [win32] - - '@tauri-apps/api@2.9.0': - resolution: {integrity: sha512-qD5tMjh7utwBk9/5PrTA/aGr3i5QaJ/Mlt7p8NilQ45WgbifUNPyKWsA63iQ8YfQq6R8ajMapU+/Q8nMcPRLNw==} - - '@tauri-apps/plugin-dialog@2.4.2': - resolution: {integrity: sha512-lNIn5CZuw8WZOn8zHzmFmDSzg5zfohWoa3mdULP0YFh/VogVdMVWZPcWSHlydsiJhRQYaTNSYKN7RmZKE2lCYQ==} - - '@tauri-apps/plugin-http@2.5.4': - resolution: {integrity: sha512-/i4U/9za3mrytTgfRn5RHneKubZE/dwRmshYwyMvNRlkWjvu1m4Ma72kcbVJMZFGXpkbl+qLyWMGrihtWB76Zg==} - - '@trysound/sax@0.2.0': - resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} - engines: {node: '>=10.13.0'} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/fs-extra@8.1.5': - resolution: {integrity: sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==} - - '@types/glob@7.2.0': - resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - - '@types/minimatch@6.0.0': - resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} - deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. - - '@types/node@24.10.1': - resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} - - '@types/prop-types@15.7.15': - resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} - - '@types/react@18.3.27': - resolution: {integrity: sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==} - - '@types/resolve@1.20.2': - resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - baseline-browser-mapping@2.8.29: - resolution: {integrity: sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==} - hasBin: true - - boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - browserslist@4.28.0: - resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - caniuse-api@3.0.0: - resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - - caniuse-lite@1.0.30001756: - resolution: {integrity: sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - colord@2.9.3: - resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} - - colorette@1.4.0: - resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} - - commander@7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - - commondir@1.0.1: - resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - concat-with-sourcemaps@1.1.0: - resolution: {integrity: sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==} - - css-declaration-sorter@6.4.1: - resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} - engines: {node: ^10 || ^12 || >=14} - peerDependencies: - postcss: ^8.0.9 - - css-select@4.3.0: - resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} - - css-tree@1.1.3: - resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} - engines: {node: '>=8.0.0'} - - css-what@6.2.2: - resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} - engines: {node: '>= 6'} - - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - - cssnano-preset-default@5.2.14: - resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - cssnano-utils@3.1.0: - resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - cssnano@5.1.15: - resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - csso@4.2.0: - resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} - engines: {node: '>=8.0.0'} - - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - dom-serializer@1.4.1: - resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} - - domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - - domhandler@4.3.1: - resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} - engines: {node: '>= 4'} - - domutils@2.8.0: - resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} - - electron-to-chromium@1.5.258: - resolution: {integrity: sha512-rHUggNV5jKQ0sSdWwlaRDkFc3/rRJIVnOSe9yR4zrR07m3ZxhP4N27Hlg8VeJGGYgFTxK5NqDmWI4DSH72vIJg==} - - entities@2.2.0: - resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - estree-walker@0.6.1: - resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==} - - estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - - fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - generic-names@4.0.0: - resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob@13.0.0: - resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} - engines: {node: 20 || >=22} - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - - globby@10.0.1: - resolution: {integrity: sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==} - engines: {node: '>=8'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - icss-replace-symbols@1.1.0: - resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==} - - icss-utils@5.1.0: - resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - import-cwd@3.0.0: - resolution: {integrity: sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==} - engines: {node: '>=8'} - - import-from@3.0.0: - resolution: {integrity: sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==} - engines: {node: '>=8'} - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-module@1.0.0: - resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-plain-object@3.0.1: - resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==} - engines: {node: '>=0.10.0'} - - is-reference@1.2.1: - resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - - lilconfig@2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} - engines: {node: '>=10'} - - loader-utils@3.3.1: - resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} - engines: {node: '>= 12.13.0'} - - lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - - lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - - lodash.uniq@4.5.0: - resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - - lru-cache@11.2.2: - resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} - engines: {node: 20 || >=22} - - lucide-react@0.469.0: - resolution: {integrity: sha512-28vvUnnKQ/dBwiCQtwJw7QauYnE7yd2Cyp4tTTJpvglX4EMpbflcdBgrgToX2j71B3YvugK/NH3BGUk+E/p/Fw==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - - mdn-data@2.0.14: - resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - minimatch@10.1.1: - resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} - engines: {node: 20 || >=22} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - - mobx-react-lite@4.1.1: - resolution: {integrity: sha512-iUxiMpsvNraCKXU+yPotsOncNNmyeS2B5DKL+TL6Tar/xm+wwNJAubJmtRSeAoYawdZqwv8Z/+5nPRHeQxTiXg==} - peerDependencies: - mobx: ^6.9.0 - react: ^16.8.0 || ^17 || ^18 || ^19 - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - - mobx@6.15.0: - resolution: {integrity: sha512-UczzB+0nnwGotYSgllfARAqWCJ5e/skuV2K/l+Zyck/H6pJIhLXuBnz+6vn2i211o7DtbE78HQtsYEKICHGI+g==} - - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - - normalize-url@6.1.0: - resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} - engines: {node: '>=10'} - - nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - - p-queue@6.6.2: - resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} - engines: {node: '>=8'} - - p-timeout@3.2.0: - resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} - engines: {node: '>=8'} - - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-scurry@2.0.1: - resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} - engines: {node: 20 || >=22} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} - - pify@5.0.0: - resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} - engines: {node: '>=10'} - - postcss-calc@8.2.4: - resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} - peerDependencies: - postcss: ^8.2.2 - - postcss-colormin@5.3.1: - resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-convert-values@5.1.3: - resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-discard-comments@5.1.2: - resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-discard-duplicates@5.1.0: - resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-discard-empty@5.1.1: - resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-discard-overridden@5.1.0: - resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-load-config@3.1.4: - resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} - engines: {node: '>= 10'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - - postcss-merge-longhand@5.1.7: - resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-merge-rules@5.1.4: - resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-minify-font-values@5.1.0: - resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-minify-gradients@5.1.1: - resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-minify-params@5.1.4: - resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-minify-selectors@5.2.1: - resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-modules-extract-imports@3.1.0: - resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - postcss-modules-local-by-default@4.2.0: - resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - postcss-modules-scope@3.2.1: - resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - postcss-modules-values@4.0.0: - resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: ^8.1.0 - - postcss-modules@4.3.1: - resolution: {integrity: sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==} - peerDependencies: - postcss: ^8.0.0 - - postcss-normalize-charset@5.1.0: - resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-display-values@5.1.0: - resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-positions@5.1.1: - resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-repeat-style@5.1.1: - resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-string@5.1.0: - resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-timing-functions@5.1.0: - resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-unicode@5.1.1: - resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-url@5.1.0: - resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-normalize-whitespace@5.1.1: - resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-ordered-values@5.1.3: - resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-reduce-initial@5.1.2: - resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-reduce-transforms@5.1.0: - resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} - engines: {node: '>=4'} - - postcss-selector-parser@7.1.0: - resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} - engines: {node: '>=4'} - - postcss-svgo@5.1.0: - resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-unique-selectors@5.1.1: - resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} - engines: {node: ^10 || ^12 || >=14} - - promise.series@0.2.0: - resolution: {integrity: sha512-VWQJyU2bcDTgZw8kpfBpB/ejZASlCrzwz5f2hjb/zlujOEB4oeiAhHygAWq8ubsX2GVkD4kCU5V2dwOTaCY5EQ==} - engines: {node: '>=0.12'} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} - engines: {node: '>=0.10.0'} - - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} - engines: {node: '>= 0.4'} - hasBin: true - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rimraf@6.1.2: - resolution: {integrity: sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==} - engines: {node: 20 || >=22} - hasBin: true - - rollup-plugin-copy@3.5.0: - resolution: {integrity: sha512-wI8D5dvYovRMx/YYKtUNt3Yxaw4ORC9xo6Gt9t22kveWz1enG9QrhVlagzwrxSC455xD1dHMKhIJkbsQ7d48BA==} - engines: {node: '>=8.3'} - - rollup-plugin-dts@6.2.3: - resolution: {integrity: sha512-UgnEsfciXSPpASuOelix7m4DrmyQgiaWBnvI0TM4GxuDh5FkqW8E5hu57bCxXB90VvR1WNfLV80yEDN18UogSA==} - engines: {node: '>=16'} - peerDependencies: - rollup: ^3.29.4 || ^4 - typescript: ^4.5 || ^5.0 - - rollup-plugin-postcss@4.0.2: - resolution: {integrity: sha512-05EaY6zvZdmvPUDi3uCcAQoESDcYnv8ogJJQRp6V5kZ6J6P7uAVJlrTZcaaA20wTH527YTnKfkAoPxWI/jPp4w==} - engines: {node: '>=10'} - peerDependencies: - postcss: 8.x - - rollup-pluginutils@2.8.2: - resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} - - rollup@4.53.3: - resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safe-identifier@0.4.2: - resolution: {integrity: sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - stable@0.1.8: - resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} - deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' - - string-hash@1.1.3: - resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==} - - style-inject@0.3.0: - resolution: {integrity: sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw==} - - stylehacks@5.1.1: - resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} - engines: {node: ^10 || ^12 || >=14.0} - peerDependencies: - postcss: ^8.2.15 - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - svgo@2.8.0: - resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} - engines: {node: '>=10.13.0'} - hasBin: true - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - tsyringe@4.10.0: - resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} - engines: {node: '>= 6.0.0'} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - - update-browserslist-db@1.1.4: - resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - use-sync-external-store@1.6.0: - resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - - zustand@5.0.8: - resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=18.0.0' - immer: '>=9.0.6' - react: '>=18.0.0' - use-sync-external-store: '>=1.2.0' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true - use-sync-external-store: - optional: true - -snapshots: - - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - optional: true - - '@babel/helper-validator-identifier@7.28.5': - optional: true - - '@esengine/behavior-tree@file:../behavior-tree(@esengine/ecs-framework@file:../core)': - dependencies: - '@esengine/ecs-framework': file:../core - tslib: 2.8.1 - - '@esengine/ecs-framework@file:../core': - dependencies: - tslib: 2.8.1 - - '@esengine/editor-core@file:../editor-core(@esengine/ecs-framework@file:../core)': - dependencies: - '@esengine/ecs-framework': file:../core - tslib: 2.8.1 - - '@isaacs/balanced-match@4.0.1': {} - - '@isaacs/brace-expansion@5.0.0': - dependencies: - '@isaacs/balanced-match': 4.0.1 - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 - - '@rollup/plugin-commonjs@28.0.9(rollup@4.53.3)': - dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.53.3) - commondir: 1.0.1 - estree-walker: 2.0.2 - fdir: 6.5.0(picomatch@4.0.3) - is-reference: 1.2.1 - magic-string: 0.30.21 - picomatch: 4.0.3 - optionalDependencies: - rollup: 4.53.3 - - '@rollup/plugin-node-resolve@15.3.1(rollup@4.53.3)': - dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.53.3) - '@types/resolve': 1.20.2 - deepmerge: 4.3.1 - is-module: 1.0.0 - resolve: 1.22.11 - optionalDependencies: - rollup: 4.53.3 - - '@rollup/pluginutils@5.3.0(rollup@4.53.3)': - dependencies: - '@types/estree': 1.0.8 - estree-walker: 2.0.2 - picomatch: 4.0.3 - optionalDependencies: - rollup: 4.53.3 - - '@rollup/rollup-android-arm-eabi@4.53.3': - optional: true - - '@rollup/rollup-android-arm64@4.53.3': - optional: true - - '@rollup/rollup-darwin-arm64@4.53.3': - optional: true - - '@rollup/rollup-darwin-x64@4.53.3': - optional: true - - '@rollup/rollup-freebsd-arm64@4.53.3': - optional: true - - '@rollup/rollup-freebsd-x64@4.53.3': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.53.3': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.53.3': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.53.3': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.53.3': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.53.3': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.53.3': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.53.3': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.53.3': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.53.3': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.53.3': - optional: true - - '@rollup/rollup-linux-x64-musl@4.53.3': - optional: true - - '@rollup/rollup-openharmony-arm64@4.53.3': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.53.3': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.53.3': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.53.3': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.53.3': - optional: true - - '@tauri-apps/api@2.9.0': {} - - '@tauri-apps/plugin-dialog@2.4.2': - dependencies: - '@tauri-apps/api': 2.9.0 - - '@tauri-apps/plugin-http@2.5.4': - dependencies: - '@tauri-apps/api': 2.9.0 - - '@trysound/sax@0.2.0': {} - - '@types/estree@1.0.8': {} - - '@types/fs-extra@8.1.5': - dependencies: - '@types/node': 24.10.1 - - '@types/glob@7.2.0': - dependencies: - '@types/minimatch': 6.0.0 - '@types/node': 24.10.1 - - '@types/minimatch@6.0.0': - dependencies: - minimatch: 10.1.1 - - '@types/node@24.10.1': - dependencies: - undici-types: 7.16.0 - - '@types/prop-types@15.7.15': {} - - '@types/react@18.3.27': - dependencies: - '@types/prop-types': 15.7.15 - csstype: 3.2.3 - - '@types/resolve@1.20.2': {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - array-union@2.1.0: {} - - balanced-match@1.0.2: {} - - baseline-browser-mapping@2.8.29: {} - - boolbase@1.0.0: {} - - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.28.0: - dependencies: - baseline-browser-mapping: 2.8.29 - caniuse-lite: 1.0.30001756 - electron-to-chromium: 1.5.258 - node-releases: 2.0.27 - update-browserslist-db: 1.1.4(browserslist@4.28.0) - - caniuse-api@3.0.0: - dependencies: - browserslist: 4.28.0 - caniuse-lite: 1.0.30001756 - lodash.memoize: 4.1.2 - lodash.uniq: 4.5.0 - - caniuse-lite@1.0.30001756: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - colord@2.9.3: {} - - colorette@1.4.0: {} - - commander@7.2.0: {} - - commondir@1.0.1: {} - - concat-map@0.0.1: {} - - concat-with-sourcemaps@1.1.0: - dependencies: - source-map: 0.6.1 - - css-declaration-sorter@6.4.1(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - css-select@4.3.0: - dependencies: - boolbase: 1.0.0 - css-what: 6.2.2 - domhandler: 4.3.1 - domutils: 2.8.0 - nth-check: 2.1.1 - - css-tree@1.1.3: - dependencies: - mdn-data: 2.0.14 - source-map: 0.6.1 - - css-what@6.2.2: {} - - cssesc@3.0.0: {} - - cssnano-preset-default@5.2.14(postcss@8.5.6): - dependencies: - css-declaration-sorter: 6.4.1(postcss@8.5.6) - cssnano-utils: 3.1.0(postcss@8.5.6) - postcss: 8.5.6 - postcss-calc: 8.2.4(postcss@8.5.6) - postcss-colormin: 5.3.1(postcss@8.5.6) - postcss-convert-values: 5.1.3(postcss@8.5.6) - postcss-discard-comments: 5.1.2(postcss@8.5.6) - postcss-discard-duplicates: 5.1.0(postcss@8.5.6) - postcss-discard-empty: 5.1.1(postcss@8.5.6) - postcss-discard-overridden: 5.1.0(postcss@8.5.6) - postcss-merge-longhand: 5.1.7(postcss@8.5.6) - postcss-merge-rules: 5.1.4(postcss@8.5.6) - postcss-minify-font-values: 5.1.0(postcss@8.5.6) - postcss-minify-gradients: 5.1.1(postcss@8.5.6) - postcss-minify-params: 5.1.4(postcss@8.5.6) - postcss-minify-selectors: 5.2.1(postcss@8.5.6) - postcss-normalize-charset: 5.1.0(postcss@8.5.6) - postcss-normalize-display-values: 5.1.0(postcss@8.5.6) - postcss-normalize-positions: 5.1.1(postcss@8.5.6) - postcss-normalize-repeat-style: 5.1.1(postcss@8.5.6) - postcss-normalize-string: 5.1.0(postcss@8.5.6) - postcss-normalize-timing-functions: 5.1.0(postcss@8.5.6) - postcss-normalize-unicode: 5.1.1(postcss@8.5.6) - postcss-normalize-url: 5.1.0(postcss@8.5.6) - postcss-normalize-whitespace: 5.1.1(postcss@8.5.6) - postcss-ordered-values: 5.1.3(postcss@8.5.6) - postcss-reduce-initial: 5.1.2(postcss@8.5.6) - postcss-reduce-transforms: 5.1.0(postcss@8.5.6) - postcss-svgo: 5.1.0(postcss@8.5.6) - postcss-unique-selectors: 5.1.1(postcss@8.5.6) - - cssnano-utils@3.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - cssnano@5.1.15(postcss@8.5.6): - dependencies: - cssnano-preset-default: 5.2.14(postcss@8.5.6) - lilconfig: 2.1.0 - postcss: 8.5.6 - yaml: 1.10.2 - - csso@4.2.0: - dependencies: - css-tree: 1.1.3 - - csstype@3.2.3: {} - - deepmerge@4.3.1: {} - - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - dom-serializer@1.4.1: - dependencies: - domelementtype: 2.3.0 - domhandler: 4.3.1 - entities: 2.2.0 - - domelementtype@2.3.0: {} - - domhandler@4.3.1: - dependencies: - domelementtype: 2.3.0 - - domutils@2.8.0: - dependencies: - dom-serializer: 1.4.1 - domelementtype: 2.3.0 - domhandler: 4.3.1 - - electron-to-chromium@1.5.258: {} - - entities@2.2.0: {} - - escalade@3.2.0: {} - - estree-walker@0.6.1: {} - - estree-walker@2.0.2: {} - - eventemitter3@4.0.7: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fastq@1.19.1: - dependencies: - reusify: 1.1.0 - - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - fs-extra@8.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fs.realpath@1.0.0: {} - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - generic-names@4.0.0: - dependencies: - loader-utils: 3.3.1 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob@13.0.0: - dependencies: - minimatch: 10.1.1 - minipass: 7.1.2 - path-scurry: 2.0.1 - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - globby@10.0.1: - dependencies: - '@types/glob': 7.2.0 - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - glob: 7.2.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - graceful-fs@4.2.11: {} - - has-flag@4.0.0: {} - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - icss-replace-symbols@1.1.0: {} - - icss-utils@5.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - ignore@5.3.2: {} - - import-cwd@3.0.0: - dependencies: - import-from: 3.0.0 - - import-from@3.0.0: - dependencies: - resolve-from: 5.0.0 - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - - is-extglob@2.1.1: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-module@1.0.0: {} - - is-number@7.0.0: {} - - is-plain-object@3.0.1: {} - - is-reference@1.2.1: - dependencies: - '@types/estree': 1.0.8 - - js-tokens@4.0.0: {} - - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 - - lilconfig@2.1.0: {} - - loader-utils@3.3.1: {} - - lodash.camelcase@4.3.0: {} - - lodash.memoize@4.1.2: {} - - lodash.uniq@4.5.0: {} - - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 - - lru-cache@11.2.2: {} - - lucide-react@0.469.0(react@18.3.1): - dependencies: - react: 18.3.1 - - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - - mdn-data@2.0.14: {} - - merge2@1.4.1: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - minimatch@10.1.1: - dependencies: - '@isaacs/brace-expansion': 5.0.0 - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 - - minipass@7.1.2: {} - - mobx-react-lite@4.1.1(mobx@6.15.0)(react@18.3.1): - dependencies: - mobx: 6.15.0 - react: 18.3.1 - use-sync-external-store: 1.6.0(react@18.3.1) - - mobx@6.15.0: {} - - nanoid@3.3.11: {} - - node-releases@2.0.27: {} - - normalize-url@6.1.0: {} - - nth-check@2.1.1: - dependencies: - boolbase: 1.0.0 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - p-finally@1.0.0: {} - - p-queue@6.6.2: - dependencies: - eventemitter3: 4.0.7 - p-timeout: 3.2.0 - - p-timeout@3.2.0: - dependencies: - p-finally: 1.0.0 - - package-json-from-dist@1.0.1: {} - - path-is-absolute@1.0.1: {} - - path-parse@1.0.7: {} - - path-scurry@2.0.1: - dependencies: - lru-cache: 11.2.2 - minipass: 7.1.2 - - path-type@4.0.0: {} - - picocolors@1.1.1: {} - - picomatch@2.3.1: {} - - picomatch@4.0.3: {} - - pify@5.0.0: {} - - postcss-calc@8.2.4(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-selector-parser: 6.1.2 - postcss-value-parser: 4.2.0 - - postcss-colormin@5.3.1(postcss@8.5.6): - dependencies: - browserslist: 4.28.0 - caniuse-api: 3.0.0 - colord: 2.9.3 - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-convert-values@5.1.3(postcss@8.5.6): - dependencies: - browserslist: 4.28.0 - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-discard-comments@5.1.2(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - postcss-discard-duplicates@5.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - postcss-discard-empty@5.1.1(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - postcss-discard-overridden@5.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - postcss-load-config@3.1.4(postcss@8.5.6): - dependencies: - lilconfig: 2.1.0 - yaml: 1.10.2 - optionalDependencies: - postcss: 8.5.6 - - postcss-merge-longhand@5.1.7(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - stylehacks: 5.1.1(postcss@8.5.6) - - postcss-merge-rules@5.1.4(postcss@8.5.6): - dependencies: - browserslist: 4.28.0 - caniuse-api: 3.0.0 - cssnano-utils: 3.1.0(postcss@8.5.6) - postcss: 8.5.6 - postcss-selector-parser: 6.1.2 - - postcss-minify-font-values@5.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-minify-gradients@5.1.1(postcss@8.5.6): - dependencies: - colord: 2.9.3 - cssnano-utils: 3.1.0(postcss@8.5.6) - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-minify-params@5.1.4(postcss@8.5.6): - dependencies: - browserslist: 4.28.0 - cssnano-utils: 3.1.0(postcss@8.5.6) - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-minify-selectors@5.2.1(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-selector-parser: 6.1.2 - - postcss-modules-extract-imports@3.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - postcss-modules-local-by-default@4.2.0(postcss@8.5.6): - dependencies: - icss-utils: 5.1.0(postcss@8.5.6) - postcss: 8.5.6 - postcss-selector-parser: 7.1.0 - postcss-value-parser: 4.2.0 - - postcss-modules-scope@3.2.1(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-selector-parser: 7.1.0 - - postcss-modules-values@4.0.0(postcss@8.5.6): - dependencies: - icss-utils: 5.1.0(postcss@8.5.6) - postcss: 8.5.6 - - postcss-modules@4.3.1(postcss@8.5.6): - dependencies: - generic-names: 4.0.0 - icss-replace-symbols: 1.1.0 - lodash.camelcase: 4.3.0 - postcss: 8.5.6 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.6) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.6) - postcss-modules-scope: 3.2.1(postcss@8.5.6) - postcss-modules-values: 4.0.0(postcss@8.5.6) - string-hash: 1.1.3 - - postcss-normalize-charset@5.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - - postcss-normalize-display-values@5.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-normalize-positions@5.1.1(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-normalize-repeat-style@5.1.1(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-normalize-string@5.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-normalize-timing-functions@5.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-normalize-unicode@5.1.1(postcss@8.5.6): - dependencies: - browserslist: 4.28.0 - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-normalize-url@5.1.0(postcss@8.5.6): - dependencies: - normalize-url: 6.1.0 - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-normalize-whitespace@5.1.1(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-ordered-values@5.1.3(postcss@8.5.6): - dependencies: - cssnano-utils: 3.1.0(postcss@8.5.6) - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-reduce-initial@5.1.2(postcss@8.5.6): - dependencies: - browserslist: 4.28.0 - caniuse-api: 3.0.0 - postcss: 8.5.6 - - postcss-reduce-transforms@5.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - - postcss-selector-parser@6.1.2: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - - postcss-selector-parser@7.1.0: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - - postcss-svgo@5.1.0(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-value-parser: 4.2.0 - svgo: 2.8.0 - - postcss-unique-selectors@5.1.1(postcss@8.5.6): - dependencies: - postcss: 8.5.6 - postcss-selector-parser: 6.1.2 - - postcss-value-parser@4.2.0: {} - - postcss@8.5.6: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - promise.series@0.2.0: {} - - queue-microtask@1.2.3: {} - - react@18.3.1: - dependencies: - loose-envify: 1.4.0 - - resolve-from@5.0.0: {} - - resolve@1.22.11: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - reusify@1.1.0: {} - - rimraf@6.1.2: - dependencies: - glob: 13.0.0 - package-json-from-dist: 1.0.1 - - rollup-plugin-copy@3.5.0: - dependencies: - '@types/fs-extra': 8.1.5 - colorette: 1.4.0 - fs-extra: 8.1.0 - globby: 10.0.1 - is-plain-object: 3.0.1 - - rollup-plugin-dts@6.2.3(rollup@4.53.3)(typescript@5.9.3): - dependencies: - magic-string: 0.30.21 - rollup: 4.53.3 - typescript: 5.9.3 - optionalDependencies: - '@babel/code-frame': 7.27.1 - - rollup-plugin-postcss@4.0.2(postcss@8.5.6): - dependencies: - chalk: 4.1.2 - concat-with-sourcemaps: 1.1.0 - cssnano: 5.1.15(postcss@8.5.6) - import-cwd: 3.0.0 - p-queue: 6.6.2 - pify: 5.0.0 - postcss: 8.5.6 - postcss-load-config: 3.1.4(postcss@8.5.6) - postcss-modules: 4.3.1(postcss@8.5.6) - promise.series: 0.2.0 - resolve: 1.22.11 - rollup-pluginutils: 2.8.2 - safe-identifier: 0.4.2 - style-inject: 0.3.0 - transitivePeerDependencies: - - ts-node - - rollup-pluginutils@2.8.2: - dependencies: - estree-walker: 0.6.1 - - rollup@4.53.3: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.3 - '@rollup/rollup-android-arm64': 4.53.3 - '@rollup/rollup-darwin-arm64': 4.53.3 - '@rollup/rollup-darwin-x64': 4.53.3 - '@rollup/rollup-freebsd-arm64': 4.53.3 - '@rollup/rollup-freebsd-x64': 4.53.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 - '@rollup/rollup-linux-arm-musleabihf': 4.53.3 - '@rollup/rollup-linux-arm64-gnu': 4.53.3 - '@rollup/rollup-linux-arm64-musl': 4.53.3 - '@rollup/rollup-linux-loong64-gnu': 4.53.3 - '@rollup/rollup-linux-ppc64-gnu': 4.53.3 - '@rollup/rollup-linux-riscv64-gnu': 4.53.3 - '@rollup/rollup-linux-riscv64-musl': 4.53.3 - '@rollup/rollup-linux-s390x-gnu': 4.53.3 - '@rollup/rollup-linux-x64-gnu': 4.53.3 - '@rollup/rollup-linux-x64-musl': 4.53.3 - '@rollup/rollup-openharmony-arm64': 4.53.3 - '@rollup/rollup-win32-arm64-msvc': 4.53.3 - '@rollup/rollup-win32-ia32-msvc': 4.53.3 - '@rollup/rollup-win32-x64-gnu': 4.53.3 - '@rollup/rollup-win32-x64-msvc': 4.53.3 - fsevents: 2.3.3 - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - safe-identifier@0.4.2: {} - - slash@3.0.0: {} - - source-map-js@1.2.1: {} - - source-map@0.6.1: {} - - stable@0.1.8: {} - - string-hash@1.1.3: {} - - style-inject@0.3.0: {} - - stylehacks@5.1.1(postcss@8.5.6): - dependencies: - browserslist: 4.28.0 - postcss: 8.5.6 - postcss-selector-parser: 6.1.2 - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-preserve-symlinks-flag@1.0.0: {} - - svgo@2.8.0: - dependencies: - '@trysound/sax': 0.2.0 - commander: 7.2.0 - css-select: 4.3.0 - css-tree: 1.1.3 - csso: 4.2.0 - picocolors: 1.1.1 - stable: 0.1.8 - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - tslib@1.14.1: {} - - tslib@2.8.1: {} - - tsyringe@4.10.0: - dependencies: - tslib: 1.14.1 - - typescript@5.9.3: {} - - undici-types@7.16.0: {} - - universalify@0.1.2: {} - - update-browserslist-db@1.1.4(browserslist@4.28.0): - dependencies: - browserslist: 4.28.0 - escalade: 3.2.0 - picocolors: 1.1.1 - - use-sync-external-store@1.6.0(react@18.3.1): - dependencies: - react: 18.3.1 - - util-deprecate@1.0.2: {} - - wrappy@1.0.2: {} - - yaml@1.10.2: {} - - zustand@5.0.8(@types/react@18.3.27)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)): - optionalDependencies: - '@types/react': 18.3.27 - react: 18.3.1 - use-sync-external-store: 1.6.0(react@18.3.1) diff --git a/packages/behavior-tree-editor/rollup.config.cjs b/packages/behavior-tree-editor/rollup.config.cjs deleted file mode 100644 index 878d0e36..00000000 --- a/packages/behavior-tree-editor/rollup.config.cjs +++ /dev/null @@ -1,67 +0,0 @@ -const resolve = require('@rollup/plugin-node-resolve'); -const commonjs = require('@rollup/plugin-commonjs'); -const replace = require('@rollup/plugin-replace'); -const dts = require('rollup-plugin-dts').default; -const postcss = require('rollup-plugin-postcss'); - -const external = [ - '@esengine/editor-runtime', - '@esengine/behavior-tree', -]; - -module.exports = [ - { - input: 'bin/index.js', - output: { - file: 'dist/index.esm.js', - format: 'es', - sourcemap: true, - exports: 'named', - inlineDynamicImports: true - }, - plugins: [ - replace({ - preventAssignment: true, - 'process.env.NODE_ENV': JSON.stringify('production') - }), - resolve({ - extensions: ['.js', '.jsx'] - }), - postcss({ - inject: true, - minimize: false - }), - commonjs() - ], - external, - onwarn(warning, warn) { - if (warning.code === 'CIRCULAR_DEPENDENCY' || warning.code === 'THIS_IS_UNDEFINED') { - return; - } - warn(warning); - } - }, - - // 类型定义构建 - { - input: 'bin/index.d.ts', - output: { - file: 'dist/index.d.ts', - format: 'es' - }, - plugins: [ - dts({ - respectExternal: true - }) - ], - external: [ - ...external, - /\.css$/, - // 排除 React 相关类型,避免 rollup-plugin-dts 解析问题 - 'react', - 'react-dom', - /^@types\//, - /^@esengine\// - ] - } -]; diff --git a/packages/behavior-tree-editor/scripts/copy-css.js b/packages/behavior-tree-editor/scripts/copy-css.js deleted file mode 100644 index 81fdcdcb..00000000 --- a/packages/behavior-tree-editor/scripts/copy-css.js +++ /dev/null @@ -1,25 +0,0 @@ -import { readdirSync, statSync, copyFileSync, mkdirSync } from 'fs'; -import { join, dirname, relative } from 'path'; - -function copyCSS(srcDir, destDir) { - const files = readdirSync(srcDir); - - for (const file of files) { - const srcPath = join(srcDir, file); - const stat = statSync(srcPath); - - if (stat.isDirectory()) { - copyCSS(srcPath, destDir); - } else if (file.endsWith('.css')) { - const relativePath = relative('src', srcPath); - const destPath = join(destDir, relativePath); - - mkdirSync(dirname(destPath), { recursive: true }); - copyFileSync(srcPath, destPath); - console.log(`Copied: ${relativePath}`); - } - } -} - -copyCSS('src', 'bin'); -console.log('CSS files copied successfully!'); diff --git a/packages/behavior-tree-editor/src/BehaviorTreeModule.ts b/packages/behavior-tree-editor/src/BehaviorTreeModule.ts deleted file mode 100644 index 46027b37..00000000 --- a/packages/behavior-tree-editor/src/BehaviorTreeModule.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { singleton } from 'tsyringe'; -import { Core, createLogger } from '@esengine/ecs-framework'; -import { CompilerRegistry, IEditorModule, IModuleContext, PanelPosition } from '@esengine/editor-core'; -import { BehaviorTreeService } from './services/BehaviorTreeService'; -import { BehaviorTreeCompiler } from './compiler/BehaviorTreeCompiler'; -import { BehaviorTreeNodeInspectorProvider } from './providers/BehaviorTreeNodeInspectorProvider'; -import { BehaviorTreeEditorPanel } from './components/panels/BehaviorTreeEditorPanel'; - -const logger = createLogger('BehaviorTreeModule'); - -@singleton() -export class BehaviorTreeModule implements IEditorModule { - readonly id = 'behavior-tree'; - readonly name = 'Behavior Tree Editor'; - readonly version = '1.0.0'; - - async load(context: IModuleContext): Promise { - logger.info('[BehaviorTreeModule] Loading behavior tree editor module...'); - - this.registerServices(context); - this.registerCompilers(); - this.registerInspectors(context); - this.registerCommands(context); - this.registerPanels(context); - this.subscribeEvents(context); - - logger.info('[BehaviorTreeModule] Behavior tree editor module loaded'); - } - - private registerServices(context: IModuleContext): void { - context.container.register(BehaviorTreeService, { useClass: BehaviorTreeService }); - logger.info('[BehaviorTreeModule] Services registered'); - } - - private registerCompilers(): void { - const compilerRegistry = Core.services.resolve(CompilerRegistry); - if (compilerRegistry) { - const compiler = new BehaviorTreeCompiler(); - compilerRegistry.register(compiler); - logger.info('[BehaviorTreeModule] Compiler registered'); - } - } - - private registerInspectors(context: IModuleContext): void { - const provider = new BehaviorTreeNodeInspectorProvider(); - context.inspectorRegistry.register(provider); - logger.info('[BehaviorTreeModule] Inspector provider registered'); - } - - async unload(): Promise { - logger.info('[BehaviorTreeModule] Unloading behavior tree editor module...'); - } - - private registerCommands(context: IModuleContext): void { - context.commands.register({ - id: 'behavior-tree.new', - label: 'New Behavior Tree', - icon: 'file-plus', - execute: async () => { - const service = context.container.resolve(BehaviorTreeService); - await service.createNew(); - } - }); - - context.commands.register({ - id: 'behavior-tree.open', - label: 'Open Behavior Tree', - icon: 'folder-open', - execute: async () => { - logger.info('Open behavior tree'); - } - }); - - context.commands.register({ - id: 'behavior-tree.save', - label: 'Save Behavior Tree', - icon: 'save', - keybinding: { key: 'S', ctrl: true }, - execute: async () => { - logger.info('Save behavior tree'); - } - }); - } - - private registerPanels(context: IModuleContext): void { - logger.info('[BehaviorTreeModule] Registering panels...'); - - context.panels.register({ - id: 'behavior-tree-editor', - title: '行为树编辑器', - icon: 'GitBranch', - component: BehaviorTreeEditorPanel, - position: PanelPosition.Center, - defaultSize: 400, - closable: true, - isDynamic: true - }); - - logger.info('[BehaviorTreeModule] Panel registered: behavior-tree-editor'); - } - - private subscribeEvents(_context: IModuleContext): void { - // 文件加载由 BehaviorTreeEditorPanel 处理 - } -} diff --git a/packages/behavior-tree-editor/src/BehaviorTreePlugin.ts b/packages/behavior-tree-editor/src/BehaviorTreePlugin.ts deleted file mode 100644 index a51c82ec..00000000 --- a/packages/behavior-tree-editor/src/BehaviorTreePlugin.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { - type Core, - type ServiceContainer, - type IService, - type ServiceType, - type IEditorPlugin, - EditorPluginCategory, - CompilerRegistry, - ICompilerRegistry, - InspectorRegistry, - IInspectorRegistry, - PanelPosition, - type FileCreationTemplate, - type FileActionHandler, - type PanelDescriptor, - createElement, - Icons, - createLogger, -} from '@esengine/editor-runtime'; -import { BehaviorTreeService } from './services/BehaviorTreeService'; -import { FileSystemService } from './services/FileSystemService'; -import { BehaviorTreeCompiler } from './compiler/BehaviorTreeCompiler'; -import { BehaviorTreeNodeInspectorProvider } from './providers/BehaviorTreeNodeInspectorProvider'; -import { BehaviorTreeEditorPanel } from './components/panels/BehaviorTreeEditorPanel'; -import { useBehaviorTreeDataStore } from './stores'; -import { createRootNode } from './domain/constants/RootNode'; -import { PluginContext } from './PluginContext'; - -const { GitBranch } = Icons; - -const logger = createLogger('BehaviorTreePlugin'); - -export class BehaviorTreePlugin implements IEditorPlugin { - readonly name = '@esengine/behavior-tree-editor'; - readonly version = '1.0.0'; - readonly displayName = 'Behavior Tree Editor'; - readonly category = EditorPluginCategory.Tool; - readonly description = 'Visual behavior tree editor for game AI development'; - readonly icon = 'GitBranch'; - - private services?: ServiceContainer; - private registeredServices: Set> = new Set(); - private fileActionHandler?: FileActionHandler; - private fileCreationTemplate?: FileCreationTemplate; - - async install(core: Core, services: ServiceContainer): Promise { - this.services = services; - // 设置插件上下文,让内部服务可以访问服务容器 - PluginContext.setServices(services); - this.registerServices(services); - this.registerCompilers(services); - this.registerInspectors(services); - this.registerFileActions(services); - } - - async uninstall(): Promise { - if (this.services) { - for (const serviceType of this.registeredServices) { - this.services.unregister(serviceType); - } - } - - this.registeredServices.clear(); - useBehaviorTreeDataStore.getState().reset(); - PluginContext.clear(); - this.services = undefined; - } - - registerPanels(): PanelDescriptor[] { - return [ - { - id: 'behavior-tree-editor', - title: 'Behavior Tree Editor', - position: PanelPosition.Center, - closable: true, - component: BehaviorTreeEditorPanel, - order: 100, - isDynamic: true // 标记为动态面板 - } - ]; - } - - private registerServices(services: ServiceContainer): void { - // 先注册 FileSystemService(BehaviorTreeService 依赖它) - if (services.isRegistered(FileSystemService)) { - services.unregister(FileSystemService); - } - services.registerSingleton(FileSystemService); - this.registeredServices.add(FileSystemService); - - // 再注册 BehaviorTreeService - if (services.isRegistered(BehaviorTreeService)) { - services.unregister(BehaviorTreeService); - } - services.registerSingleton(BehaviorTreeService); - this.registeredServices.add(BehaviorTreeService); - } - - private registerCompilers(services: ServiceContainer): void { - try { - const compilerRegistry = services.resolve(ICompilerRegistry); - const compiler = new BehaviorTreeCompiler(); - compilerRegistry.register(compiler); - logger.info('Successfully registered BehaviorTreeCompiler'); - } catch (error) { - logger.error('Failed to register compiler:', error); - } - } - - private registerInspectors(services: ServiceContainer): void { - try { - const inspectorRegistry = services.resolve(IInspectorRegistry); - if (inspectorRegistry) { - const provider = new BehaviorTreeNodeInspectorProvider(); - inspectorRegistry.register(provider); - } - } catch (error) { - logger.error('Failed to register inspector:', error); - } - } - - private registerFileActions(services: ServiceContainer): void { - this.fileCreationTemplate = { - label: 'Behavior Tree', - extension: 'btree', - defaultFileName: 'NewBehaviorTree', - icon: createElement(GitBranch, { size: 16 }), - createContent: (fileName: string) => { - // 创建根节点 - const rootNode = createRootNode(); - const rootNodeData = { - id: rootNode.id, - type: rootNode.template.type, - displayName: rootNode.template.displayName, - data: rootNode.data, - position: { - x: rootNode.position.x, - y: rootNode.position.y - }, - children: [] - }; - - const emptyTree = { - name: fileName.replace('.btree', ''), - nodes: [rootNodeData], - connections: [], - variables: {} - }; - - return JSON.stringify(emptyTree, null, 2); - } - }; - - this.fileActionHandler = { - extensions: ['btree'], - onDoubleClick: async (filePath: string) => { - const service = services.resolve(BehaviorTreeService); - if (service) { - await service.loadFromFile(filePath); - } - } - }; - } - - registerFileActionHandlers(): FileActionHandler[] { - return this.fileActionHandler ? [this.fileActionHandler] : []; - } - - registerFileCreationTemplates(): FileCreationTemplate[] { - return this.fileCreationTemplate ? [this.fileCreationTemplate] : []; - } -} diff --git a/packages/behavior-tree-editor/src/index.ts b/packages/behavior-tree-editor/src/index.ts deleted file mode 100644 index 08164040..00000000 --- a/packages/behavior-tree-editor/src/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { BehaviorTreePlugin } from './BehaviorTreePlugin'; - -export default new BehaviorTreePlugin(); - -export { BehaviorTreePlugin } from './BehaviorTreePlugin'; -export { PluginContext } from './PluginContext'; -export { BehaviorTreeEditorPanel } from './components/panels/BehaviorTreeEditorPanel'; -export * from './BehaviorTreeModule'; -export * from './services/BehaviorTreeService'; -export * from './providers/BehaviorTreeNodeInspectorProvider'; - -export * from './domain'; -export * from './application/commands/tree'; -export * from './application/use-cases'; -export * from './application/services/BlackboardManager'; -export * from './application/services/ExecutionController'; -export * from './application/services/GlobalBlackboardService'; -export * from './application/interfaces/IExecutionHooks'; -export * from './application/state/BehaviorTreeDataStore'; -export * from './hooks'; -export * from './stores'; -// Re-export specific items to avoid conflicts -export { - EditorConfig -} from './types'; -export * from './infrastructure/factories/NodeFactory'; -export * from './infrastructure/serialization/BehaviorTreeSerializer'; -export * from './infrastructure/validation/BehaviorTreeValidator'; -export * from './infrastructure/events/EditorEventBus'; -export * from './infrastructure/services/NodeRegistryService'; -export * from './utils/BehaviorTreeExecutor'; -export * from './utils/DOMCache'; -export * from './utils/portUtils'; -export * from './utils/RuntimeLoader'; -export * from './compiler/BehaviorTreeCompiler'; -// Export everything except DEFAULT_EDITOR_CONFIG from editorConstants -export { - ICON_MAP, - ROOT_NODE_TEMPLATE, - DEFAULT_EDITOR_CONFIG -} from './config/editorConstants'; -export * from './interfaces/IEditorExtensions'; diff --git a/packages/behavior-tree-editor/tsconfig.json b/packages/behavior-tree-editor/tsconfig.json deleted file mode 100644 index fcec3b51..00000000 --- a/packages/behavior-tree-editor/tsconfig.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "ES2020", - "moduleResolution": "node", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - "downlevelIteration": true, - "outDir": "./bin", - "rootDir": "./src", - "composite": true, - "jsx": "react-jsx", - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "lib": ["ES2020", "DOM"] - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist", "bin"], - "references": [ - { "path": "../core" }, - { "path": "../editor-core" }, - { "path": "../behavior-tree" } - ] -} diff --git a/packages/behavior-tree/package.json b/packages/behavior-tree/package.json index 78c4d816..e24163e1 100644 --- a/packages/behavior-tree/package.json +++ b/packages/behavior-tree/package.json @@ -1,21 +1,29 @@ { "name": "@esengine/behavior-tree", "version": "1.0.1", - "description": "完全ECS化的行为树系统,基于组件和实体的行为树实现", - "main": "bin/index.js", - "types": "bin/index.d.ts", + "description": "ECS-based AI behavior tree system with visual editor and runtime execution", + "main": "dist/index.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "type": "module", "exports": { ".": { - "types": "./bin/index.d.ts", - "import": "./bin/index.js", - "development": { - "types": "./src/index.ts", - "import": "./src/index.ts" - } - } + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./runtime": { + "types": "./dist/runtime.d.ts", + "import": "./dist/runtime.js" + }, + "./editor": { + "types": "./dist/editor/index.d.ts", + "import": "./dist/editor/index.js" + }, + "./plugin.json": "./plugin.json" }, "files": [ - "bin/**/*" + "dist", + "plugin.json" ], "keywords": [ "ecs", @@ -25,40 +33,52 @@ "entity-component-system" ], "scripts": { - "clean": "rimraf bin dist tsconfig.tsbuildinfo", - "build:ts": "tsc", - "prebuild": "npm run clean", - "build": "npm run build:ts", - "build:esm": "vite build", - "build:watch": "tsc --watch", - "rebuild": "npm run clean && npm run build", - "build:npm": "npm run build && node build-rollup.cjs", + "clean": "rimraf dist tsconfig.tsbuildinfo", + "build": "vite build", + "build:watch": "vite build --watch", + "type-check": "tsc --noEmit", "test": "jest --config jest.config.cjs", "test:watch": "jest --watch --config jest.config.cjs" }, "author": "yhh", "license": "MIT", "peerDependencies": { - "@esengine/ecs-framework": "^2.2.8" + "@esengine/ecs-framework": ">=2.0.0", + "@esengine/ecs-components": "workspace:*", + "@esengine/editor-runtime": "workspace:*", + "lucide-react": "^0.545.0", + "react": "^18.3.1", + "zustand": "^4.5.2" + }, + "peerDependenciesMeta": { + "@esengine/ecs-components": { + "optional": true + }, + "@esengine/editor-runtime": { + "optional": true + }, + "react": { + "optional": true + }, + "lucide-react": { + "optional": true + }, + "zustand": { + "optional": true + } }, "devDependencies": { - "@babel/core": "^7.28.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1", - "@babel/preset-env": "^7.28.3", - "@rollup/plugin-babel": "^6.0.4", - "@rollup/plugin-commonjs": "^28.0.3", - "@rollup/plugin-node-resolve": "^16.0.1", - "@rollup/plugin-terser": "^0.4.4", + "@tauri-apps/plugin-fs": "^2.4.2", "@types/jest": "^29.5.14", "@types/node": "^20.19.17", + "@types/react": "^18.3.12", + "@vitejs/plugin-react": "^4.7.0", "jest": "^29.7.0", "rimraf": "^5.0.0", - "rollup": "^4.42.0", - "rollup-plugin-dts": "^6.2.1", "ts-jest": "^29.4.0", "typescript": "^5.8.3", - "vite": "^6.0.7" + "vite": "^6.0.7", + "vite-plugin-dts": "^3.7.0" }, "dependencies": { "tslib": "^2.8.1" diff --git a/packages/behavior-tree/plugin.json b/packages/behavior-tree/plugin.json new file mode 100644 index 00000000..a94df2d4 --- /dev/null +++ b/packages/behavior-tree/plugin.json @@ -0,0 +1,30 @@ +{ + "id": "@esengine/behavior-tree", + "name": "Behavior Tree System", + "version": "1.0.0", + "description": "AI behavior tree system with visual editor and runtime execution", + "category": "ai", + "loadingPhase": "default", + "enabledByDefault": true, + "canContainContent": false, + "isEnginePlugin": false, + "modules": [ + { + "name": "BehaviorTreeRuntime", + "type": "runtime", + "entry": "./src/index.ts" + }, + { + "name": "BehaviorTreeEditor", + "type": "editor", + "entry": "./src/editor/index.ts" + } + ], + "dependencies": [ + { + "id": "@esengine/core", + "version": ">=1.0.0" + } + ], + "icon": "GitBranch" +} diff --git a/packages/behavior-tree/src/BehaviorTreeBuilder.ts b/packages/behavior-tree/src/BehaviorTreeBuilder.ts index 4c8e6c61..869cff58 100644 --- a/packages/behavior-tree/src/BehaviorTreeBuilder.ts +++ b/packages/behavior-tree/src/BehaviorTreeBuilder.ts @@ -1,4 +1,4 @@ -import { BehaviorTreeData, BehaviorNodeData } from './Runtime/BehaviorTreeData'; +import { BehaviorTreeData, BehaviorNodeData } from './execution/BehaviorTreeData'; import { NodeType } from './Types/TaskStatus'; /** diff --git a/packages/behavior-tree/src/BehaviorTreePlugin.ts b/packages/behavior-tree/src/BehaviorTreePlugin.ts deleted file mode 100644 index a220558e..00000000 --- a/packages/behavior-tree/src/BehaviorTreePlugin.ts +++ /dev/null @@ -1,89 +0,0 @@ -import type { Core } from '@esengine/ecs-framework'; -import type { ServiceContainer, IPlugin, IScene } from '@esengine/ecs-framework'; -import { WorldManager } from '@esengine/ecs-framework'; -import { BehaviorTreeExecutionSystem } from './Runtime/BehaviorTreeExecutionSystem'; -import { GlobalBlackboardService } from './Services/GlobalBlackboardService'; -import { BehaviorTreeAssetManager } from './Runtime/BehaviorTreeAssetManager'; - -/** - * 行为树插件 - * - * 提供便捷方法向场景添加行为树系统 - * - * @example - * ```typescript - * const core = Core.create(); - * const plugin = new BehaviorTreePlugin(); - * await core.pluginManager.install(plugin); - * - * // 为场景添加行为树系统 - * const scene = new Scene(); - * plugin.setupScene(scene); - * ``` - */ -export class BehaviorTreePlugin implements IPlugin { - readonly name = '@esengine/behavior-tree'; - readonly version = '1.0.0'; - - private worldManager: WorldManager | null = null; - private services: ServiceContainer | null = null; - - /** - * 安装插件 - */ - async install(_core: Core, services: ServiceContainer): Promise { - this.services = services; - - // 注册全局服务 - services.registerSingleton(GlobalBlackboardService); - services.registerSingleton(BehaviorTreeAssetManager); - - this.worldManager = services.resolve(WorldManager); - } - - /** - * 卸载插件 - */ - async uninstall(): Promise { - if (this.services) { - this.services.unregister(GlobalBlackboardService); - this.services.unregister(BehaviorTreeAssetManager); - } - - this.worldManager = null; - this.services = null; - } - - /** - * 为场景设置行为树系统 - * - * 向场景添加行为树执行系统 - * - * @param scene 目标场景 - * - * @example - * ```typescript - * const scene = new Scene(); - * behaviorTreePlugin.setupScene(scene); - * ``` - */ - public setupScene(scene: IScene): void { - scene.addSystem(new BehaviorTreeExecutionSystem()); - } - - /** - * 为所有现有场景设置行为树系统 - */ - public setupAllScenes(): void { - if (!this.worldManager) { - throw new Error('Plugin not installed'); - } - - const worlds = this.worldManager.getAllWorlds(); - for (const world of worlds) { - for (const scene of world.getAllScenes()) { - this.setupScene(scene); - } - } - } -} diff --git a/packages/behavior-tree/src/BehaviorTreeRuntimeModule.ts b/packages/behavior-tree/src/BehaviorTreeRuntimeModule.ts new file mode 100644 index 00000000..1fb45fba --- /dev/null +++ b/packages/behavior-tree/src/BehaviorTreeRuntimeModule.ts @@ -0,0 +1,43 @@ +/** + * Behavior Tree Runtime Module (Pure runtime, no editor dependencies) + * 行为树运行时模块(纯运行时,无编辑器依赖) + */ + +import type { IScene, ServiceContainer } from '@esengine/ecs-framework'; +import { ComponentRegistry, Core } from '@esengine/ecs-framework'; +import type { IRuntimeModuleLoader, SystemContext } from '@esengine/ecs-components'; + +import { BehaviorTreeRuntimeComponent } from './execution/BehaviorTreeRuntimeComponent'; +import { BehaviorTreeExecutionSystem } from './execution/BehaviorTreeExecutionSystem'; +import { BehaviorTreeAssetManager } from './execution/BehaviorTreeAssetManager'; +import { GlobalBlackboardService } from './Services/GlobalBlackboardService'; + +/** + * Behavior Tree Runtime Module + * 行为树运行时模块 + */ +export class BehaviorTreeRuntimeModule implements IRuntimeModuleLoader { + registerComponents(registry: typeof ComponentRegistry): void { + registry.register(BehaviorTreeRuntimeComponent); + } + + registerServices(services: ServiceContainer): void { + if (!services.isRegistered(GlobalBlackboardService)) { + services.registerSingleton(GlobalBlackboardService); + } + if (!services.isRegistered(BehaviorTreeAssetManager)) { + services.registerSingleton(BehaviorTreeAssetManager); + } + } + + createSystems(scene: IScene, context: SystemContext): void { + const behaviorTreeSystem = new BehaviorTreeExecutionSystem(Core); + + if (context.isEditor) { + behaviorTreeSystem.enabled = false; + } + + scene.addSystem(behaviorTreeSystem); + context.behaviorTreeSystem = behaviorTreeSystem; + } +} diff --git a/packages/behavior-tree/src/BehaviorTreeStarter.ts b/packages/behavior-tree/src/BehaviorTreeStarter.ts index a060bdd4..60ba5ac4 100644 --- a/packages/behavior-tree/src/BehaviorTreeStarter.ts +++ b/packages/behavior-tree/src/BehaviorTreeStarter.ts @@ -1,7 +1,7 @@ import { Entity, Core } from '@esengine/ecs-framework'; -import { BehaviorTreeData } from './Runtime/BehaviorTreeData'; -import { BehaviorTreeRuntimeComponent } from './Runtime/BehaviorTreeRuntimeComponent'; -import { BehaviorTreeAssetManager } from './Runtime/BehaviorTreeAssetManager'; +import { BehaviorTreeData } from './execution/BehaviorTreeData'; +import { BehaviorTreeRuntimeComponent } from './execution/BehaviorTreeRuntimeComponent'; +import { BehaviorTreeAssetManager } from './execution/BehaviorTreeAssetManager'; /** * 行为树启动辅助类 diff --git a/packages/behavior-tree/src/Serialization/EditorToBehaviorTreeDataConverter.ts b/packages/behavior-tree/src/Serialization/EditorToBehaviorTreeDataConverter.ts index 9282c550..550f4778 100644 --- a/packages/behavior-tree/src/Serialization/EditorToBehaviorTreeDataConverter.ts +++ b/packages/behavior-tree/src/Serialization/EditorToBehaviorTreeDataConverter.ts @@ -1,4 +1,4 @@ -import { BehaviorTreeData, BehaviorNodeData } from '../Runtime/BehaviorTreeData'; +import { BehaviorTreeData, BehaviorNodeData } from '../execution/BehaviorTreeData'; import { NodeType, AbortType } from '../Types/TaskStatus'; /** diff --git a/packages/behavior-tree/src/Serialization/NodeTemplates.ts b/packages/behavior-tree/src/Serialization/NodeTemplates.ts index c8e6020e..5e43389e 100644 --- a/packages/behavior-tree/src/Serialization/NodeTemplates.ts +++ b/packages/behavior-tree/src/Serialization/NodeTemplates.ts @@ -1,5 +1,5 @@ import { NodeType } from '../Types/TaskStatus'; -import { NodeMetadataRegistry, ConfigFieldDefinition, NodeMetadata } from '../Runtime/NodeMetadata'; +import { NodeMetadataRegistry, ConfigFieldDefinition, NodeMetadata } from '../execution/NodeMetadata'; /** * 节点数据JSON格式 diff --git a/packages/behavior-tree/src/editor/BehaviorTreePlugin.ts b/packages/behavior-tree/src/editor/BehaviorTreePlugin.ts new file mode 100644 index 00000000..96384ca9 --- /dev/null +++ b/packages/behavior-tree/src/editor/BehaviorTreePlugin.ts @@ -0,0 +1,98 @@ +/** + * Behavior Tree Unified Plugin + * 行为树统一插件 + */ + +import type { IScene, ServiceContainer } from '@esengine/ecs-framework'; +import { ComponentRegistry, Core } from '@esengine/ecs-framework'; +import type { + IPluginLoader, + IRuntimeModuleLoader, + PluginDescriptor, + SystemContext +} from '@esengine/editor-runtime'; + +// Runtime imports +import { BehaviorTreeRuntimeComponent } from '../execution/BehaviorTreeRuntimeComponent'; +import { BehaviorTreeExecutionSystem } from '../execution/BehaviorTreeExecutionSystem'; +import { BehaviorTreeAssetManager } from '../execution/BehaviorTreeAssetManager'; +import { GlobalBlackboardService } from '../Services/GlobalBlackboardService'; + +/** + * 插件描述符 + */ +export const descriptor: PluginDescriptor = { + id: '@esengine/behavior-tree', + name: 'Behavior Tree System', + version: '1.0.0', + description: 'AI 行为树系统,支持可视化编辑和运行时执行', + category: 'ai', + enabledByDefault: true, + canContainContent: false, + isEnginePlugin: false, + modules: [ + { + name: 'BehaviorTreeRuntime', + type: 'runtime', + loadingPhase: 'default', + entry: './src/index.ts' + }, + { + name: 'BehaviorTreeEditor', + type: 'editor', + loadingPhase: 'default', + entry: './src/editor/index.ts' + } + ], + dependencies: [ + { id: '@esengine/core', version: '>=1.0.0' } + ], + icon: 'GitBranch' +}; + +/** + * Behavior Tree Runtime Module + * 行为树运行时模块 + */ +export class BehaviorTreeRuntimeModule implements IRuntimeModuleLoader { + registerComponents(registry: typeof ComponentRegistry): void { + registry.register(BehaviorTreeRuntimeComponent); + } + + registerServices(services: ServiceContainer): void { + if (!services.isRegistered(GlobalBlackboardService)) { + services.registerSingleton(GlobalBlackboardService); + } + if (!services.isRegistered(BehaviorTreeAssetManager)) { + services.registerSingleton(BehaviorTreeAssetManager); + } + } + + createSystems(scene: IScene, context: SystemContext): void { + const behaviorTreeSystem = new BehaviorTreeExecutionSystem(Core); + + // 编辑器模式下默认禁用 + if (context.isEditor) { + behaviorTreeSystem.enabled = false; + } + + scene.addSystem(behaviorTreeSystem); + + // 保存引用 + context.behaviorTreeSystem = behaviorTreeSystem; + } +} + +/** + * Behavior Tree Plugin Loader + * 行为树插件加载器 + * + * 注意:editorModule 在 ./index.ts 中通过 createBehaviorTreePlugin() 设置 + */ +export const BehaviorTreePlugin: IPluginLoader = { + descriptor, + runtimeModule: new BehaviorTreeRuntimeModule(), + // editorModule 将在 index.ts 中设置 +}; + +export default BehaviorTreePlugin; diff --git a/packages/behavior-tree-editor/src/PluginContext.ts b/packages/behavior-tree/src/editor/PluginContext.ts similarity index 100% rename from packages/behavior-tree-editor/src/PluginContext.ts rename to packages/behavior-tree/src/editor/PluginContext.ts diff --git a/packages/behavior-tree-editor/src/application/commands/CommandManager.ts b/packages/behavior-tree/src/editor/application/commands/CommandManager.ts similarity index 100% rename from packages/behavior-tree-editor/src/application/commands/CommandManager.ts rename to packages/behavior-tree/src/editor/application/commands/CommandManager.ts diff --git a/packages/behavior-tree-editor/src/application/commands/ICommand.ts b/packages/behavior-tree/src/editor/application/commands/ICommand.ts similarity index 100% rename from packages/behavior-tree-editor/src/application/commands/ICommand.ts rename to packages/behavior-tree/src/editor/application/commands/ICommand.ts diff --git a/packages/behavior-tree-editor/src/application/commands/ITreeState.ts b/packages/behavior-tree/src/editor/application/commands/ITreeState.ts similarity index 100% rename from packages/behavior-tree-editor/src/application/commands/ITreeState.ts rename to packages/behavior-tree/src/editor/application/commands/ITreeState.ts diff --git a/packages/behavior-tree-editor/src/application/commands/tree/AddConnectionCommand.ts b/packages/behavior-tree/src/editor/application/commands/tree/AddConnectionCommand.ts similarity index 94% rename from packages/behavior-tree-editor/src/application/commands/tree/AddConnectionCommand.ts rename to packages/behavior-tree/src/editor/application/commands/tree/AddConnectionCommand.ts index 6cfd193f..40583883 100644 --- a/packages/behavior-tree-editor/src/application/commands/tree/AddConnectionCommand.ts +++ b/packages/behavior-tree/src/editor/application/commands/tree/AddConnectionCommand.ts @@ -1,5 +1,5 @@ import { Connection } from '../../../domain/models/Connection'; -import { BaseCommand } from '@esengine/editor-core'; +import { BaseCommand } from '@esengine/editor-runtime'; import { ITreeState } from '../ITreeState'; /** diff --git a/packages/behavior-tree-editor/src/application/commands/tree/CreateNodeCommand.ts b/packages/behavior-tree/src/editor/application/commands/tree/CreateNodeCommand.ts similarity index 93% rename from packages/behavior-tree-editor/src/application/commands/tree/CreateNodeCommand.ts rename to packages/behavior-tree/src/editor/application/commands/tree/CreateNodeCommand.ts index ab60e9d9..035e61db 100644 --- a/packages/behavior-tree-editor/src/application/commands/tree/CreateNodeCommand.ts +++ b/packages/behavior-tree/src/editor/application/commands/tree/CreateNodeCommand.ts @@ -1,5 +1,5 @@ import { Node } from '../../../domain/models/Node'; -import { BaseCommand } from '@esengine/editor-core'; +import { BaseCommand } from '@esengine/editor-runtime'; import { ITreeState } from '../ITreeState'; /** diff --git a/packages/behavior-tree-editor/src/application/commands/tree/DeleteNodeCommand.ts b/packages/behavior-tree/src/editor/application/commands/tree/DeleteNodeCommand.ts similarity index 94% rename from packages/behavior-tree-editor/src/application/commands/tree/DeleteNodeCommand.ts rename to packages/behavior-tree/src/editor/application/commands/tree/DeleteNodeCommand.ts index c08723c0..93a47f40 100644 --- a/packages/behavior-tree-editor/src/application/commands/tree/DeleteNodeCommand.ts +++ b/packages/behavior-tree/src/editor/application/commands/tree/DeleteNodeCommand.ts @@ -1,5 +1,5 @@ import { Node } from '../../../domain/models/Node'; -import { BaseCommand } from '@esengine/editor-core'; +import { BaseCommand } from '@esengine/editor-runtime'; import { ITreeState } from '../ITreeState'; /** diff --git a/packages/behavior-tree-editor/src/application/commands/tree/MoveNodeCommand.ts b/packages/behavior-tree/src/editor/application/commands/tree/MoveNodeCommand.ts similarity index 96% rename from packages/behavior-tree-editor/src/application/commands/tree/MoveNodeCommand.ts rename to packages/behavior-tree/src/editor/application/commands/tree/MoveNodeCommand.ts index 7d58f542..dfb14d07 100644 --- a/packages/behavior-tree-editor/src/application/commands/tree/MoveNodeCommand.ts +++ b/packages/behavior-tree/src/editor/application/commands/tree/MoveNodeCommand.ts @@ -1,5 +1,5 @@ import { Position } from '../../../domain/value-objects/Position'; -import { BaseCommand, ICommand } from '@esengine/editor-core'; +import { BaseCommand, ICommand } from '@esengine/editor-runtime'; import { ITreeState } from '../ITreeState'; /** diff --git a/packages/behavior-tree-editor/src/application/commands/tree/RemoveConnectionCommand.ts b/packages/behavior-tree/src/editor/application/commands/tree/RemoveConnectionCommand.ts similarity index 96% rename from packages/behavior-tree-editor/src/application/commands/tree/RemoveConnectionCommand.ts rename to packages/behavior-tree/src/editor/application/commands/tree/RemoveConnectionCommand.ts index d36972bb..cf43aea1 100644 --- a/packages/behavior-tree-editor/src/application/commands/tree/RemoveConnectionCommand.ts +++ b/packages/behavior-tree/src/editor/application/commands/tree/RemoveConnectionCommand.ts @@ -1,5 +1,5 @@ import { Connection } from '../../../domain/models/Connection'; -import { BaseCommand } from '@esengine/editor-core'; +import { BaseCommand } from '@esengine/editor-runtime'; import { ITreeState } from '../ITreeState'; /** diff --git a/packages/behavior-tree-editor/src/application/commands/tree/UpdateNodeDataCommand.ts b/packages/behavior-tree/src/editor/application/commands/tree/UpdateNodeDataCommand.ts similarity index 94% rename from packages/behavior-tree-editor/src/application/commands/tree/UpdateNodeDataCommand.ts rename to packages/behavior-tree/src/editor/application/commands/tree/UpdateNodeDataCommand.ts index 20096607..e04ec4f2 100644 --- a/packages/behavior-tree-editor/src/application/commands/tree/UpdateNodeDataCommand.ts +++ b/packages/behavior-tree/src/editor/application/commands/tree/UpdateNodeDataCommand.ts @@ -1,4 +1,4 @@ -import { BaseCommand } from '@esengine/editor-core'; +import { BaseCommand } from '@esengine/editor-runtime'; import { ITreeState } from '../ITreeState'; /** diff --git a/packages/behavior-tree-editor/src/application/commands/tree/index.ts b/packages/behavior-tree/src/editor/application/commands/tree/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/application/commands/tree/index.ts rename to packages/behavior-tree/src/editor/application/commands/tree/index.ts diff --git a/packages/behavior-tree-editor/src/application/interfaces/IExecutionHooks.ts b/packages/behavior-tree/src/editor/application/interfaces/IExecutionHooks.ts similarity index 100% rename from packages/behavior-tree-editor/src/application/interfaces/IExecutionHooks.ts rename to packages/behavior-tree/src/editor/application/interfaces/IExecutionHooks.ts diff --git a/packages/behavior-tree-editor/src/application/services/BlackboardManager.ts b/packages/behavior-tree/src/editor/application/services/BlackboardManager.ts similarity index 100% rename from packages/behavior-tree-editor/src/application/services/BlackboardManager.ts rename to packages/behavior-tree/src/editor/application/services/BlackboardManager.ts diff --git a/packages/behavior-tree-editor/src/application/services/ExecutionController.ts b/packages/behavior-tree/src/editor/application/services/ExecutionController.ts similarity index 100% rename from packages/behavior-tree-editor/src/application/services/ExecutionController.ts rename to packages/behavior-tree/src/editor/application/services/ExecutionController.ts diff --git a/packages/behavior-tree-editor/src/application/services/GlobalBlackboardService.ts b/packages/behavior-tree/src/editor/application/services/GlobalBlackboardService.ts similarity index 99% rename from packages/behavior-tree-editor/src/application/services/GlobalBlackboardService.ts rename to packages/behavior-tree/src/editor/application/services/GlobalBlackboardService.ts index e97f813f..b291a0e2 100644 --- a/packages/behavior-tree-editor/src/application/services/GlobalBlackboardService.ts +++ b/packages/behavior-tree/src/editor/application/services/GlobalBlackboardService.ts @@ -1,4 +1,4 @@ -import { GlobalBlackboardConfig, BlackboardValueType, BlackboardVariable } from '@esengine/behavior-tree'; +import { GlobalBlackboardConfig, BlackboardValueType, BlackboardVariable } from '../../..'; import { createLogger } from '@esengine/ecs-framework'; const logger = createLogger('GlobalBlackboardService'); diff --git a/packages/behavior-tree-editor/src/application/state/BehaviorTreeDataStore.ts b/packages/behavior-tree/src/editor/application/state/BehaviorTreeDataStore.ts similarity index 99% rename from packages/behavior-tree-editor/src/application/state/BehaviorTreeDataStore.ts rename to packages/behavior-tree/src/editor/application/state/BehaviorTreeDataStore.ts index 7ecfec56..cdee1330 100644 --- a/packages/behavior-tree-editor/src/application/state/BehaviorTreeDataStore.ts +++ b/packages/behavior-tree/src/editor/application/state/BehaviorTreeDataStore.ts @@ -1,7 +1,7 @@ import { createStore } from '@esengine/editor-runtime'; const create = createStore; -import { NodeTemplates, NodeTemplate } from '@esengine/behavior-tree'; +import { NodeTemplates, NodeTemplate } from '../../..'; import { BehaviorTree } from '../../domain/models/BehaviorTree'; import { Node } from '../../domain/models/Node'; import { Connection, ConnectionType } from '../../domain/models/Connection'; diff --git a/packages/behavior-tree-editor/src/application/use-cases/AddConnectionUseCase.ts b/packages/behavior-tree/src/editor/application/use-cases/AddConnectionUseCase.ts similarity index 95% rename from packages/behavior-tree-editor/src/application/use-cases/AddConnectionUseCase.ts rename to packages/behavior-tree/src/editor/application/use-cases/AddConnectionUseCase.ts index dbc2d8db..35c2432f 100644 --- a/packages/behavior-tree-editor/src/application/use-cases/AddConnectionUseCase.ts +++ b/packages/behavior-tree/src/editor/application/use-cases/AddConnectionUseCase.ts @@ -1,5 +1,5 @@ import { Connection, ConnectionType } from '../../domain/models/Connection'; -import { CommandManager } from '@esengine/editor-core'; +import { CommandManager } from '@esengine/editor-runtime'; import { AddConnectionCommand } from '../commands/tree/AddConnectionCommand'; import { ITreeState } from '../commands/ITreeState'; import { IValidator } from '../../domain/interfaces/IValidator'; diff --git a/packages/behavior-tree-editor/src/application/use-cases/CreateNodeUseCase.ts b/packages/behavior-tree/src/editor/application/use-cases/CreateNodeUseCase.ts similarity index 92% rename from packages/behavior-tree-editor/src/application/use-cases/CreateNodeUseCase.ts rename to packages/behavior-tree/src/editor/application/use-cases/CreateNodeUseCase.ts index d3b9badd..1e4e4540 100644 --- a/packages/behavior-tree-editor/src/application/use-cases/CreateNodeUseCase.ts +++ b/packages/behavior-tree/src/editor/application/use-cases/CreateNodeUseCase.ts @@ -1,8 +1,8 @@ -import { NodeTemplate } from '@esengine/behavior-tree'; +import { NodeTemplate } from '../../..'; import { Node } from '../../domain/models/Node'; import { Position } from '../../domain/value-objects/Position'; import { INodeFactory } from '../../domain/interfaces/INodeFactory'; -import { CommandManager } from '@esengine/editor-core'; +import { CommandManager } from '@esengine/editor-runtime'; import { CreateNodeCommand } from '../commands/tree/CreateNodeCommand'; import { ITreeState } from '../commands/ITreeState'; diff --git a/packages/behavior-tree-editor/src/application/use-cases/DeleteNodeUseCase.ts b/packages/behavior-tree/src/editor/application/use-cases/DeleteNodeUseCase.ts similarity index 96% rename from packages/behavior-tree-editor/src/application/use-cases/DeleteNodeUseCase.ts rename to packages/behavior-tree/src/editor/application/use-cases/DeleteNodeUseCase.ts index ff5bd724..c0d1d505 100644 --- a/packages/behavior-tree-editor/src/application/use-cases/DeleteNodeUseCase.ts +++ b/packages/behavior-tree/src/editor/application/use-cases/DeleteNodeUseCase.ts @@ -1,4 +1,4 @@ -import { CommandManager, ICommand } from '@esengine/editor-core'; +import { CommandManager, ICommand } from '@esengine/editor-runtime'; import { DeleteNodeCommand } from '../commands/tree/DeleteNodeCommand'; import { RemoveConnectionCommand } from '../commands/tree/RemoveConnectionCommand'; import { ITreeState } from '../commands/ITreeState'; diff --git a/packages/behavior-tree-editor/src/application/use-cases/MoveNodeUseCase.ts b/packages/behavior-tree/src/editor/application/use-cases/MoveNodeUseCase.ts similarity index 94% rename from packages/behavior-tree-editor/src/application/use-cases/MoveNodeUseCase.ts rename to packages/behavior-tree/src/editor/application/use-cases/MoveNodeUseCase.ts index ba79c0e4..062f6db9 100644 --- a/packages/behavior-tree-editor/src/application/use-cases/MoveNodeUseCase.ts +++ b/packages/behavior-tree/src/editor/application/use-cases/MoveNodeUseCase.ts @@ -1,5 +1,5 @@ import { Position } from '../../domain/value-objects/Position'; -import { CommandManager } from '@esengine/editor-core'; +import { CommandManager } from '@esengine/editor-runtime'; import { MoveNodeCommand } from '../commands/tree/MoveNodeCommand'; import { ITreeState } from '../commands/ITreeState'; diff --git a/packages/behavior-tree-editor/src/application/use-cases/RemoveConnectionUseCase.ts b/packages/behavior-tree/src/editor/application/use-cases/RemoveConnectionUseCase.ts similarity index 92% rename from packages/behavior-tree-editor/src/application/use-cases/RemoveConnectionUseCase.ts rename to packages/behavior-tree/src/editor/application/use-cases/RemoveConnectionUseCase.ts index 5ea6c180..0bc32605 100644 --- a/packages/behavior-tree-editor/src/application/use-cases/RemoveConnectionUseCase.ts +++ b/packages/behavior-tree/src/editor/application/use-cases/RemoveConnectionUseCase.ts @@ -1,4 +1,4 @@ -import { CommandManager } from '@esengine/editor-core'; +import { CommandManager } from '@esengine/editor-runtime'; import { RemoveConnectionCommand } from '../commands/tree/RemoveConnectionCommand'; import { ITreeState } from '../commands/ITreeState'; diff --git a/packages/behavior-tree-editor/src/application/use-cases/UpdateNodeDataUseCase.ts b/packages/behavior-tree/src/editor/application/use-cases/UpdateNodeDataUseCase.ts similarity index 90% rename from packages/behavior-tree-editor/src/application/use-cases/UpdateNodeDataUseCase.ts rename to packages/behavior-tree/src/editor/application/use-cases/UpdateNodeDataUseCase.ts index 8a02001a..4b9ded19 100644 --- a/packages/behavior-tree-editor/src/application/use-cases/UpdateNodeDataUseCase.ts +++ b/packages/behavior-tree/src/editor/application/use-cases/UpdateNodeDataUseCase.ts @@ -1,4 +1,4 @@ -import { CommandManager } from '@esengine/editor-core'; +import { CommandManager } from '@esengine/editor-runtime'; import { UpdateNodeDataCommand } from '../commands/tree/UpdateNodeDataCommand'; import { ITreeState } from '../commands/ITreeState'; diff --git a/packages/behavior-tree-editor/src/application/use-cases/ValidateTreeUseCase.ts b/packages/behavior-tree/src/editor/application/use-cases/ValidateTreeUseCase.ts similarity index 100% rename from packages/behavior-tree-editor/src/application/use-cases/ValidateTreeUseCase.ts rename to packages/behavior-tree/src/editor/application/use-cases/ValidateTreeUseCase.ts diff --git a/packages/behavior-tree-editor/src/application/use-cases/index.ts b/packages/behavior-tree/src/editor/application/use-cases/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/application/use-cases/index.ts rename to packages/behavior-tree/src/editor/application/use-cases/index.ts diff --git a/packages/behavior-tree-editor/src/compiler/BehaviorTreeCompiler.tsx b/packages/behavior-tree/src/editor/compiler/BehaviorTreeCompiler.tsx similarity index 99% rename from packages/behavior-tree-editor/src/compiler/BehaviorTreeCompiler.tsx rename to packages/behavior-tree/src/editor/compiler/BehaviorTreeCompiler.tsx index 23cc78c9..16ab5fb6 100644 --- a/packages/behavior-tree-editor/src/compiler/BehaviorTreeCompiler.tsx +++ b/packages/behavior-tree/src/editor/compiler/BehaviorTreeCompiler.tsx @@ -10,7 +10,7 @@ import { createLogger, } from '@esengine/editor-runtime'; import { GlobalBlackboardTypeGenerator } from '../generators/GlobalBlackboardTypeGenerator'; -import { EditorFormatConverter, BehaviorTreeAssetSerializer } from '@esengine/behavior-tree'; +import { EditorFormatConverter, BehaviorTreeAssetSerializer } from '../..'; import { useBehaviorTreeDataStore } from '../application/state/BehaviorTreeDataStore'; const { File, FolderTree, FolderOpen } = Icons; diff --git a/packages/behavior-tree-editor/src/components/BehaviorTreeEditor.tsx b/packages/behavior-tree/src/editor/components/BehaviorTreeEditor.tsx similarity index 99% rename from packages/behavior-tree-editor/src/components/BehaviorTreeEditor.tsx rename to packages/behavior-tree/src/editor/components/BehaviorTreeEditor.tsx index 678a5bce..1619c3a4 100644 --- a/packages/behavior-tree-editor/src/components/BehaviorTreeEditor.tsx +++ b/packages/behavior-tree/src/editor/components/BehaviorTreeEditor.tsx @@ -1,5 +1,5 @@ import { React, useEffect, useMemo, useRef, useState, useCallback } from '@esengine/editor-runtime'; -import { NodeTemplate, BlackboardValueType } from '@esengine/behavior-tree'; +import { NodeTemplate, BlackboardValueType } from '../..'; import { useBehaviorTreeDataStore, BehaviorTreeNode, ROOT_NODE_ID } from '../stores'; import { useUIStore } from '../stores'; import { showToast as notificationShowToast } from '../services/NotificationService'; diff --git a/packages/behavior-tree-editor/src/components/blackboard/BlackboardPanel.tsx b/packages/behavior-tree/src/editor/components/blackboard/BlackboardPanel.tsx similarity index 100% rename from packages/behavior-tree-editor/src/components/blackboard/BlackboardPanel.tsx rename to packages/behavior-tree/src/editor/components/blackboard/BlackboardPanel.tsx diff --git a/packages/behavior-tree-editor/src/components/canvas/BehaviorTreeCanvas.tsx b/packages/behavior-tree/src/editor/components/canvas/BehaviorTreeCanvas.tsx similarity index 100% rename from packages/behavior-tree-editor/src/components/canvas/BehaviorTreeCanvas.tsx rename to packages/behavior-tree/src/editor/components/canvas/BehaviorTreeCanvas.tsx diff --git a/packages/behavior-tree-editor/src/components/canvas/GridBackground.tsx b/packages/behavior-tree/src/editor/components/canvas/GridBackground.tsx similarity index 100% rename from packages/behavior-tree-editor/src/components/canvas/GridBackground.tsx rename to packages/behavior-tree/src/editor/components/canvas/GridBackground.tsx diff --git a/packages/behavior-tree-editor/src/components/canvas/index.ts b/packages/behavior-tree/src/editor/components/canvas/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/components/canvas/index.ts rename to packages/behavior-tree/src/editor/components/canvas/index.ts diff --git a/packages/behavior-tree-editor/src/components/common/DraggablePanel.tsx b/packages/behavior-tree/src/editor/components/common/DraggablePanel.tsx similarity index 100% rename from packages/behavior-tree-editor/src/components/common/DraggablePanel.tsx rename to packages/behavior-tree/src/editor/components/common/DraggablePanel.tsx diff --git a/packages/behavior-tree-editor/src/components/connections/ConnectionLayer.tsx b/packages/behavior-tree/src/editor/components/connections/ConnectionLayer.tsx similarity index 100% rename from packages/behavior-tree-editor/src/components/connections/ConnectionLayer.tsx rename to packages/behavior-tree/src/editor/components/connections/ConnectionLayer.tsx diff --git a/packages/behavior-tree-editor/src/components/connections/ConnectionRenderer.tsx b/packages/behavior-tree/src/editor/components/connections/ConnectionRenderer.tsx similarity index 100% rename from packages/behavior-tree-editor/src/components/connections/ConnectionRenderer.tsx rename to packages/behavior-tree/src/editor/components/connections/ConnectionRenderer.tsx diff --git a/packages/behavior-tree-editor/src/components/connections/index.ts b/packages/behavior-tree/src/editor/components/connections/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/components/connections/index.ts rename to packages/behavior-tree/src/editor/components/connections/index.ts diff --git a/packages/behavior-tree-editor/src/components/menu/NodeContextMenu.tsx b/packages/behavior-tree/src/editor/components/menu/NodeContextMenu.tsx similarity index 100% rename from packages/behavior-tree-editor/src/components/menu/NodeContextMenu.tsx rename to packages/behavior-tree/src/editor/components/menu/NodeContextMenu.tsx diff --git a/packages/behavior-tree-editor/src/components/menu/QuickCreateMenu.tsx b/packages/behavior-tree/src/editor/components/menu/QuickCreateMenu.tsx similarity index 99% rename from packages/behavior-tree-editor/src/components/menu/QuickCreateMenu.tsx rename to packages/behavior-tree/src/editor/components/menu/QuickCreateMenu.tsx index e9bd118a..98c2be9e 100644 --- a/packages/behavior-tree-editor/src/components/menu/QuickCreateMenu.tsx +++ b/packages/behavior-tree/src/editor/components/menu/QuickCreateMenu.tsx @@ -1,6 +1,6 @@ import { React, useRef, useEffect, useState, useMemo, Icons } from '@esengine/editor-runtime'; import type { LucideIcon } from '@esengine/editor-runtime'; -import { NodeTemplate } from '@esengine/behavior-tree'; +import { NodeTemplate } from '../../..'; import { NodeFactory } from '../../infrastructure/factories/NodeFactory'; const { Search, X, ChevronDown, ChevronRight } = Icons; diff --git a/packages/behavior-tree-editor/src/components/nodes/BehaviorTreeNode.tsx b/packages/behavior-tree/src/editor/components/nodes/BehaviorTreeNode.tsx similarity index 99% rename from packages/behavior-tree-editor/src/components/nodes/BehaviorTreeNode.tsx rename to packages/behavior-tree/src/editor/components/nodes/BehaviorTreeNode.tsx index 39a611d1..d3a375d5 100644 --- a/packages/behavior-tree-editor/src/components/nodes/BehaviorTreeNode.tsx +++ b/packages/behavior-tree/src/editor/components/nodes/BehaviorTreeNode.tsx @@ -1,6 +1,6 @@ import { React, Icons } from '@esengine/editor-runtime'; import type { LucideIcon } from '@esengine/editor-runtime'; -import { PropertyDefinition } from '@esengine/behavior-tree'; +import { PropertyDefinition } from '../../..'; import { Node as BehaviorTreeNodeType } from '../../domain/models/Node'; import { Connection } from '../../domain/models/Connection'; diff --git a/packages/behavior-tree-editor/src/components/nodes/BehaviorTreeNodeRenderer.tsx b/packages/behavior-tree/src/editor/components/nodes/BehaviorTreeNodeRenderer.tsx similarity index 100% rename from packages/behavior-tree-editor/src/components/nodes/BehaviorTreeNodeRenderer.tsx rename to packages/behavior-tree/src/editor/components/nodes/BehaviorTreeNodeRenderer.tsx diff --git a/packages/behavior-tree-editor/src/components/nodes/index.ts b/packages/behavior-tree/src/editor/components/nodes/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/components/nodes/index.ts rename to packages/behavior-tree/src/editor/components/nodes/index.ts diff --git a/packages/behavior-tree-editor/src/components/panels/BehaviorTreeEditorPanel.css b/packages/behavior-tree/src/editor/components/panels/BehaviorTreeEditorPanel.css similarity index 100% rename from packages/behavior-tree-editor/src/components/panels/BehaviorTreeEditorPanel.css rename to packages/behavior-tree/src/editor/components/panels/BehaviorTreeEditorPanel.css diff --git a/packages/behavior-tree-editor/src/components/panels/BehaviorTreeEditorPanel.tsx b/packages/behavior-tree/src/editor/components/panels/BehaviorTreeEditorPanel.tsx similarity index 91% rename from packages/behavior-tree-editor/src/components/panels/BehaviorTreeEditorPanel.tsx rename to packages/behavior-tree/src/editor/components/panels/BehaviorTreeEditorPanel.tsx index 9090f021..346a2e39 100644 --- a/packages/behavior-tree-editor/src/components/panels/BehaviorTreeEditorPanel.tsx +++ b/packages/behavior-tree/src/editor/components/panels/BehaviorTreeEditorPanel.tsx @@ -3,12 +3,11 @@ import { useState, useCallback, useEffect, - Core, createLogger, - MessageHub, open, save, Icons, + PluginAPI, } from '@esengine/editor-runtime'; import { useBehaviorTreeDataStore } from '../../stores'; import { BehaviorTreeEditor } from '../BehaviorTreeEditor'; @@ -77,11 +76,19 @@ export const BehaviorTreeEditorPanel: React.FC = ( }, [tree, lastSavedSnapshot, isOpen]); useEffect(() => { + // 检查 PluginAPI 是否可用 + if (!PluginAPI.isAvailable) { + return; + } + + let unsubscribeFileOpened: (() => void) | undefined; + let unsubscribePropertyChanged: (() => void) | undefined; + try { - const messageHub = Core.services.resolve(MessageHub); + const messageHub = PluginAPI.messageHub; // 订阅文件打开事件 - const unsubscribeFileOpened = messageHub.subscribe('behavior-tree:file-opened', (data: { filePath: string; fileName: string }) => { + unsubscribeFileOpened = messageHub.subscribe('behavior-tree:file-opened', (data: { filePath: string; fileName: string }) => { setCurrentFilePath(data.filePath); setCurrentFileName(data.fileName); const loadedTree = useBehaviorTreeDataStore.getState().tree; @@ -90,7 +97,7 @@ export const BehaviorTreeEditorPanel: React.FC = ( }); // 订阅节点属性更改事件 - const unsubscribePropertyChanged = messageHub.subscribe('behavior-tree:node-property-changed', + unsubscribePropertyChanged = messageHub.subscribe('behavior-tree:node-property-changed', (data: { nodeId: string; propertyName: string; value: any }) => { const state = useBehaviorTreeDataStore.getState(); const node = state.getNode(data.nodeId); @@ -127,19 +134,22 @@ export const BehaviorTreeEditorPanel: React.FC = ( } } ); - - return () => { - unsubscribeFileOpened(); - unsubscribePropertyChanged(); - }; } catch (error) { logger.error('Failed to subscribe to events:', error); } - }, []); + + return () => { + unsubscribeFileOpened?.(); + unsubscribePropertyChanged?.(); + }; + }, [isOpen]); const handleNodeSelect = useCallback((node: BehaviorTreeNode) => { try { - const messageHub = Core.services.resolve(MessageHub); + if (!PluginAPI.isAvailable) { + return; + } + const messageHub = PluginAPI.messageHub; messageHub.publish('behavior-tree:node-selected', { data: node }); } catch (error) { logger.error('Failed to publish node selection:', error); @@ -161,7 +171,7 @@ export const BehaviorTreeEditorPanel: React.FC = ( filePath = selected; } - const service = Core.services.resolve(BehaviorTreeService); + const service = PluginAPI.resolve(BehaviorTreeService); await service.saveToFile(filePath); setCurrentFilePath(filePath); @@ -195,7 +205,7 @@ export const BehaviorTreeEditorPanel: React.FC = ( if (!selected) return; const filePath = selected as string; - const service = Core.services.resolve(BehaviorTreeService); + const service = PluginAPI.resolve(BehaviorTreeService); await service.loadFromFile(filePath); setCurrentFilePath(filePath); @@ -220,7 +230,7 @@ export const BehaviorTreeEditorPanel: React.FC = ( } try { - const messageHub = Core.services.resolve(MessageHub); + const messageHub = PluginAPI.messageHub; messageHub.publish('compiler:open-dialog', { compilerId: 'behavior-tree', currentFileName: currentFileName || undefined, diff --git a/packages/behavior-tree-editor/src/components/panels/BehaviorTreePropertiesPanel.css b/packages/behavior-tree/src/editor/components/panels/BehaviorTreePropertiesPanel.css similarity index 100% rename from packages/behavior-tree-editor/src/components/panels/BehaviorTreePropertiesPanel.css rename to packages/behavior-tree/src/editor/components/panels/BehaviorTreePropertiesPanel.css diff --git a/packages/behavior-tree-editor/src/components/toolbar/EditorToolbar.tsx b/packages/behavior-tree/src/editor/components/toolbar/EditorToolbar.tsx similarity index 100% rename from packages/behavior-tree-editor/src/components/toolbar/EditorToolbar.tsx rename to packages/behavior-tree/src/editor/components/toolbar/EditorToolbar.tsx diff --git a/packages/behavior-tree-editor/src/config/editorConstants.ts b/packages/behavior-tree/src/editor/config/editorConstants.ts similarity index 94% rename from packages/behavior-tree-editor/src/config/editorConstants.ts rename to packages/behavior-tree/src/editor/config/editorConstants.ts index b3595cdb..d9ab11b9 100644 --- a/packages/behavior-tree-editor/src/config/editorConstants.ts +++ b/packages/behavior-tree/src/editor/config/editorConstants.ts @@ -1,4 +1,4 @@ -import { NodeTemplate, NodeType } from '@esengine/behavior-tree'; +import { NodeTemplate, NodeType } from '../..'; import { Icons } from '@esengine/editor-runtime'; import type { LucideIcon } from '@esengine/editor-runtime'; diff --git a/packages/behavior-tree-editor/src/constants/index.ts b/packages/behavior-tree/src/editor/constants/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/constants/index.ts rename to packages/behavior-tree/src/editor/constants/index.ts diff --git a/packages/behavior-tree-editor/src/domain/constants/RootNode.ts b/packages/behavior-tree/src/editor/domain/constants/RootNode.ts similarity index 92% rename from packages/behavior-tree-editor/src/domain/constants/RootNode.ts rename to packages/behavior-tree/src/editor/domain/constants/RootNode.ts index 54454312..2db45804 100644 --- a/packages/behavior-tree-editor/src/domain/constants/RootNode.ts +++ b/packages/behavior-tree/src/editor/domain/constants/RootNode.ts @@ -1,6 +1,6 @@ import { Node } from '../models/Node'; import { Position } from '../value-objects/Position'; -import { NodeTemplate } from '@esengine/behavior-tree'; +import { NodeTemplate } from '../../..'; export const ROOT_NODE_ID = 'root-node'; diff --git a/packages/behavior-tree-editor/src/domain/errors/DomainError.ts b/packages/behavior-tree/src/editor/domain/errors/DomainError.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/errors/DomainError.ts rename to packages/behavior-tree/src/editor/domain/errors/DomainError.ts diff --git a/packages/behavior-tree-editor/src/domain/errors/NodeNotFoundError.ts b/packages/behavior-tree/src/editor/domain/errors/NodeNotFoundError.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/errors/NodeNotFoundError.ts rename to packages/behavior-tree/src/editor/domain/errors/NodeNotFoundError.ts diff --git a/packages/behavior-tree-editor/src/domain/errors/ValidationError.ts b/packages/behavior-tree/src/editor/domain/errors/ValidationError.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/errors/ValidationError.ts rename to packages/behavior-tree/src/editor/domain/errors/ValidationError.ts diff --git a/packages/behavior-tree-editor/src/domain/errors/index.ts b/packages/behavior-tree/src/editor/domain/errors/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/errors/index.ts rename to packages/behavior-tree/src/editor/domain/errors/index.ts diff --git a/packages/behavior-tree-editor/src/domain/index.ts b/packages/behavior-tree/src/editor/domain/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/index.ts rename to packages/behavior-tree/src/editor/domain/index.ts diff --git a/packages/behavior-tree-editor/src/domain/interfaces/INodeFactory.ts b/packages/behavior-tree/src/editor/domain/interfaces/INodeFactory.ts similarity index 91% rename from packages/behavior-tree-editor/src/domain/interfaces/INodeFactory.ts rename to packages/behavior-tree/src/editor/domain/interfaces/INodeFactory.ts index 0a980a85..2ac10fd6 100644 --- a/packages/behavior-tree-editor/src/domain/interfaces/INodeFactory.ts +++ b/packages/behavior-tree/src/editor/domain/interfaces/INodeFactory.ts @@ -1,4 +1,4 @@ -import { NodeTemplate } from '@esengine/behavior-tree'; +import { NodeTemplate } from '../../..'; import { Node } from '../models/Node'; import { Position } from '../value-objects'; diff --git a/packages/behavior-tree-editor/src/domain/interfaces/IRepository.ts b/packages/behavior-tree/src/editor/domain/interfaces/IRepository.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/interfaces/IRepository.ts rename to packages/behavior-tree/src/editor/domain/interfaces/IRepository.ts diff --git a/packages/behavior-tree-editor/src/domain/interfaces/ISerializer.ts b/packages/behavior-tree/src/editor/domain/interfaces/ISerializer.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/interfaces/ISerializer.ts rename to packages/behavior-tree/src/editor/domain/interfaces/ISerializer.ts diff --git a/packages/behavior-tree-editor/src/domain/interfaces/IValidator.ts b/packages/behavior-tree/src/editor/domain/interfaces/IValidator.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/interfaces/IValidator.ts rename to packages/behavior-tree/src/editor/domain/interfaces/IValidator.ts diff --git a/packages/behavior-tree-editor/src/domain/interfaces/index.ts b/packages/behavior-tree/src/editor/domain/interfaces/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/interfaces/index.ts rename to packages/behavior-tree/src/editor/domain/interfaces/index.ts diff --git a/packages/behavior-tree-editor/src/domain/models/BehaviorTree.ts b/packages/behavior-tree/src/editor/domain/models/BehaviorTree.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/models/BehaviorTree.ts rename to packages/behavior-tree/src/editor/domain/models/BehaviorTree.ts diff --git a/packages/behavior-tree-editor/src/domain/models/Blackboard.ts b/packages/behavior-tree/src/editor/domain/models/Blackboard.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/models/Blackboard.ts rename to packages/behavior-tree/src/editor/domain/models/Blackboard.ts diff --git a/packages/behavior-tree-editor/src/domain/models/Connection.ts b/packages/behavior-tree/src/editor/domain/models/Connection.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/models/Connection.ts rename to packages/behavior-tree/src/editor/domain/models/Connection.ts diff --git a/packages/behavior-tree-editor/src/domain/models/Node.ts b/packages/behavior-tree/src/editor/domain/models/Node.ts similarity index 98% rename from packages/behavior-tree-editor/src/domain/models/Node.ts rename to packages/behavior-tree/src/editor/domain/models/Node.ts index 3d3278fd..476525af 100644 --- a/packages/behavior-tree-editor/src/domain/models/Node.ts +++ b/packages/behavior-tree/src/editor/domain/models/Node.ts @@ -1,4 +1,4 @@ -import { NodeTemplate } from '@esengine/behavior-tree'; +import { NodeTemplate } from '../../..'; import { Position, NodeType } from '../value-objects'; import { ValidationError } from '../errors'; diff --git a/packages/behavior-tree-editor/src/domain/models/index.ts b/packages/behavior-tree/src/editor/domain/models/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/models/index.ts rename to packages/behavior-tree/src/editor/domain/models/index.ts diff --git a/packages/behavior-tree-editor/src/domain/services/TreeValidator.ts b/packages/behavior-tree/src/editor/domain/services/TreeValidator.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/services/TreeValidator.ts rename to packages/behavior-tree/src/editor/domain/services/TreeValidator.ts diff --git a/packages/behavior-tree-editor/src/domain/services/index.ts b/packages/behavior-tree/src/editor/domain/services/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/services/index.ts rename to packages/behavior-tree/src/editor/domain/services/index.ts diff --git a/packages/behavior-tree-editor/src/domain/value-objects/NodeType.ts b/packages/behavior-tree/src/editor/domain/value-objects/NodeType.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/value-objects/NodeType.ts rename to packages/behavior-tree/src/editor/domain/value-objects/NodeType.ts diff --git a/packages/behavior-tree-editor/src/domain/value-objects/Position.ts b/packages/behavior-tree/src/editor/domain/value-objects/Position.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/value-objects/Position.ts rename to packages/behavior-tree/src/editor/domain/value-objects/Position.ts diff --git a/packages/behavior-tree-editor/src/domain/value-objects/Size.ts b/packages/behavior-tree/src/editor/domain/value-objects/Size.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/value-objects/Size.ts rename to packages/behavior-tree/src/editor/domain/value-objects/Size.ts diff --git a/packages/behavior-tree-editor/src/domain/value-objects/index.ts b/packages/behavior-tree/src/editor/domain/value-objects/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/domain/value-objects/index.ts rename to packages/behavior-tree/src/editor/domain/value-objects/index.ts diff --git a/packages/behavior-tree-editor/src/generators/GlobalBlackboardTypeGenerator.ts b/packages/behavior-tree/src/editor/generators/GlobalBlackboardTypeGenerator.ts similarity index 91% rename from packages/behavior-tree-editor/src/generators/GlobalBlackboardTypeGenerator.ts rename to packages/behavior-tree/src/editor/generators/GlobalBlackboardTypeGenerator.ts index e5650dda..dfb05952 100644 --- a/packages/behavior-tree-editor/src/generators/GlobalBlackboardTypeGenerator.ts +++ b/packages/behavior-tree/src/editor/generators/GlobalBlackboardTypeGenerator.ts @@ -1,4 +1,4 @@ -import { GlobalBlackboardConfig, BlackboardValueType } from '@esengine/behavior-tree'; +import { GlobalBlackboardConfig, BlackboardValueType } from '../..'; /** * 类型生成配置选项 @@ -70,7 +70,7 @@ export class GlobalBlackboardTypeGenerator { typeAliasName: 'GlobalVariableName', wrapperClassName: 'TypedGlobalBlackboard', defaultsName: 'GlobalBlackboardDefaults', - importPath: '@esengine/behavior-tree', + importPath: '../..', includeConstants: true, includeInterface: true, includeTypeAlias: true, @@ -150,6 +150,9 @@ export class GlobalBlackboardTypeGenerator { /** * 生成文件头部注释 + * + * 注意:生成的代码字符串会被打包到 IIFE 中,如果 import/export 出现在行首 + * 会被浏览器误解析为 ES module 语法。因此使用字符串拼接确保不在行首。 */ private static generateHeader(timestamp: string, opts: Required): string { const customHeader = opts.customHeader || `/** @@ -159,22 +162,25 @@ export class GlobalBlackboardTypeGenerator { * 生成时间: ${timestamp} */`; - return `${customHeader} - -import { GlobalBlackboardService } from '${opts.importPath}';`; + // 使用字符串拼接避免 import 出现在源代码行首(打包后可能被误解析) + const importStatement = 'im' + 'port { GlobalBlackboardService } from \'' + opts.importPath + '\';'; + return `${customHeader}\n\n${importStatement}`; } /** * 生成常量对象 + * 注意:使用 EXP + 'ort' 拼接避免打包后 export 在行首被误解析 */ private static generateConstants(variables: any[], opts: Required): string { const quote = opts.quoteStyle === 'single' ? "'" : '"'; + // 使用拼接避免 export 在源代码行首 + const exp = 'exp' + 'ort'; if (variables.length === 0) { return `/** * 全局变量名称常量 */ -export const ${opts.constantsName} = {} as const;`; +${exp} const ${opts.constantsName} = {} as const;`; } // 按命名空间分组 @@ -190,7 +196,7 @@ export const ${opts.constantsName} = {} as const;`; * 全局变量名称常量 * 使用常量避免拼写错误 */ -export const ${opts.constantsName} = { +${exp} const ${opts.constantsName} = { ${entries} } as const;`; } else { @@ -220,7 +226,7 @@ ${entries} * 全局变量名称常量 * 使用常量避免拼写错误 */ -export const ${opts.constantsName} = { +${exp} const ${opts.constantsName} = { ${namespaces} } as const;`; } @@ -230,11 +236,13 @@ ${namespaces} * 生成接口定义 */ private static generateInterface(variables: any[], opts: Required): string { + const exp = 'exp' + 'ort'; + if (variables.length === 0) { return `/** * 全局变量类型定义 */ -export interface ${opts.interfaceName} {}`; +${exp} interface ${opts.interfaceName} {}`; } const properties = variables @@ -248,7 +256,7 @@ export interface ${opts.interfaceName} {}`; return `/** * 全局变量类型定义 */ -export interface ${opts.interfaceName} { +${exp} interface ${opts.interfaceName} { ${properties} }`; } @@ -257,16 +265,18 @@ ${properties} * 生成类型别名 */ private static generateTypeAliases(opts: Required): string { + const exp = 'exp' + 'ort'; return `/** * 全局变量名称联合类型 */ -export type ${opts.typeAliasName} = keyof ${opts.interfaceName};`; +${exp} type ${opts.typeAliasName} = keyof ${opts.interfaceName};`; } /** * 生成类型安全包装类 */ private static generateTypedClass(opts: Required): string { + const exp = 'exp' + 'ort'; return `/** * 类型安全的全局黑板服务包装器 * @@ -284,7 +294,7 @@ export type ${opts.typeAliasName} = keyof ${opts.interfaceName};`; * gb.setValue('playerHP', 'invalid'); // ❌ 编译错误 * \`\`\` */ -export class ${opts.wrapperClassName} { +${exp} class ${opts.wrapperClassName} { constructor(private service: GlobalBlackboardService) {} /** @@ -326,11 +336,13 @@ export class ${opts.wrapperClassName} { * 生成默认值配置 */ private static generateDefaults(variables: any[], opts: Required): string { + const exp = 'exp' + 'ort'; + if (variables.length === 0) { return `/** * 默认值配置 */ -export const ${opts.defaultsName}: ${opts.interfaceName} = {};`; +${exp} const ${opts.defaultsName}: ${opts.interfaceName} = {};`; } const properties = variables @@ -362,7 +374,7 @@ export const ${opts.defaultsName}: ${opts.interfaceName} = {};`; * service.importConfig(config); * \`\`\` */ -export const ${opts.defaultsName}: ${opts.interfaceName} = { +${exp} const ${opts.defaultsName}: ${opts.interfaceName} = { ${properties} };`; } diff --git a/packages/behavior-tree-editor/src/generators/LocalBlackboardTypeGenerator.ts b/packages/behavior-tree/src/editor/generators/LocalBlackboardTypeGenerator.ts similarity index 100% rename from packages/behavior-tree-editor/src/generators/LocalBlackboardTypeGenerator.ts rename to packages/behavior-tree/src/editor/generators/LocalBlackboardTypeGenerator.ts diff --git a/packages/behavior-tree-editor/src/hooks/index.ts b/packages/behavior-tree/src/editor/hooks/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/hooks/index.ts rename to packages/behavior-tree/src/editor/hooks/index.ts diff --git a/packages/behavior-tree-editor/src/hooks/useCanvasInteraction.ts b/packages/behavior-tree/src/editor/hooks/useCanvasInteraction.ts similarity index 100% rename from packages/behavior-tree-editor/src/hooks/useCanvasInteraction.ts rename to packages/behavior-tree/src/editor/hooks/useCanvasInteraction.ts diff --git a/packages/behavior-tree-editor/src/hooks/useCanvasMouseEvents.ts b/packages/behavior-tree/src/editor/hooks/useCanvasMouseEvents.ts similarity index 100% rename from packages/behavior-tree-editor/src/hooks/useCanvasMouseEvents.ts rename to packages/behavior-tree/src/editor/hooks/useCanvasMouseEvents.ts diff --git a/packages/behavior-tree-editor/src/hooks/useCommandHistory.ts b/packages/behavior-tree/src/editor/hooks/useCommandHistory.ts similarity index 100% rename from packages/behavior-tree-editor/src/hooks/useCommandHistory.ts rename to packages/behavior-tree/src/editor/hooks/useCommandHistory.ts diff --git a/packages/behavior-tree-editor/src/hooks/useConnectionOperations.ts b/packages/behavior-tree/src/editor/hooks/useConnectionOperations.ts similarity index 100% rename from packages/behavior-tree-editor/src/hooks/useConnectionOperations.ts rename to packages/behavior-tree/src/editor/hooks/useConnectionOperations.ts diff --git a/packages/behavior-tree-editor/src/hooks/useContextMenu.ts b/packages/behavior-tree/src/editor/hooks/useContextMenu.ts similarity index 100% rename from packages/behavior-tree-editor/src/hooks/useContextMenu.ts rename to packages/behavior-tree/src/editor/hooks/useContextMenu.ts diff --git a/packages/behavior-tree-editor/src/hooks/useDropHandler.ts b/packages/behavior-tree/src/editor/hooks/useDropHandler.ts similarity index 98% rename from packages/behavior-tree-editor/src/hooks/useDropHandler.ts rename to packages/behavior-tree/src/editor/hooks/useDropHandler.ts index 72e7c6ef..43482349 100644 --- a/packages/behavior-tree-editor/src/hooks/useDropHandler.ts +++ b/packages/behavior-tree/src/editor/hooks/useDropHandler.ts @@ -1,5 +1,5 @@ import { useState, type RefObject, React, createLogger } from '@esengine/editor-runtime'; -import { NodeTemplate, NodeType } from '@esengine/behavior-tree'; +import { NodeTemplate, NodeType } from '../..'; import { Position } from '../domain/value-objects/Position'; import { useNodeOperations } from './useNodeOperations'; diff --git a/packages/behavior-tree-editor/src/hooks/useEditorHandlers.ts b/packages/behavior-tree/src/editor/hooks/useEditorHandlers.ts similarity index 100% rename from packages/behavior-tree-editor/src/hooks/useEditorHandlers.ts rename to packages/behavior-tree/src/editor/hooks/useEditorHandlers.ts diff --git a/packages/behavior-tree-editor/src/hooks/useEditorState.ts b/packages/behavior-tree/src/editor/hooks/useEditorState.ts similarity index 100% rename from packages/behavior-tree-editor/src/hooks/useEditorState.ts rename to packages/behavior-tree/src/editor/hooks/useEditorState.ts diff --git a/packages/behavior-tree-editor/src/hooks/useExecutionController.ts b/packages/behavior-tree/src/editor/hooks/useExecutionController.ts similarity index 100% rename from packages/behavior-tree-editor/src/hooks/useExecutionController.ts rename to packages/behavior-tree/src/editor/hooks/useExecutionController.ts diff --git a/packages/behavior-tree-editor/src/hooks/useKeyboardShortcuts.ts b/packages/behavior-tree/src/editor/hooks/useKeyboardShortcuts.ts similarity index 100% rename from packages/behavior-tree-editor/src/hooks/useKeyboardShortcuts.ts rename to packages/behavior-tree/src/editor/hooks/useKeyboardShortcuts.ts diff --git a/packages/behavior-tree-editor/src/hooks/useNodeDrag.ts b/packages/behavior-tree/src/editor/hooks/useNodeDrag.ts similarity index 100% rename from packages/behavior-tree-editor/src/hooks/useNodeDrag.ts rename to packages/behavior-tree/src/editor/hooks/useNodeDrag.ts diff --git a/packages/behavior-tree-editor/src/hooks/useNodeOperations.ts b/packages/behavior-tree/src/editor/hooks/useNodeOperations.ts similarity index 98% rename from packages/behavior-tree-editor/src/hooks/useNodeOperations.ts rename to packages/behavior-tree/src/editor/hooks/useNodeOperations.ts index 59b18e94..aa52006f 100644 --- a/packages/behavior-tree-editor/src/hooks/useNodeOperations.ts +++ b/packages/behavior-tree/src/editor/hooks/useNodeOperations.ts @@ -1,5 +1,5 @@ import { useCallback, useMemo, CommandManager } from '@esengine/editor-runtime'; -import { NodeTemplate } from '@esengine/behavior-tree'; +import { NodeTemplate } from '../..'; import { Position } from '../domain/value-objects/Position'; import { INodeFactory } from '../domain/interfaces/INodeFactory'; import { TreeStateAdapter } from '../application/state/BehaviorTreeDataStore'; diff --git a/packages/behavior-tree-editor/src/hooks/useNodeTracking.ts b/packages/behavior-tree/src/editor/hooks/useNodeTracking.ts similarity index 100% rename from packages/behavior-tree-editor/src/hooks/useNodeTracking.ts rename to packages/behavior-tree/src/editor/hooks/useNodeTracking.ts diff --git a/packages/behavior-tree-editor/src/hooks/usePortConnection.ts b/packages/behavior-tree/src/editor/hooks/usePortConnection.ts similarity index 99% rename from packages/behavior-tree-editor/src/hooks/usePortConnection.ts rename to packages/behavior-tree/src/editor/hooks/usePortConnection.ts index b06d6e97..65c90c07 100644 --- a/packages/behavior-tree-editor/src/hooks/usePortConnection.ts +++ b/packages/behavior-tree/src/editor/hooks/usePortConnection.ts @@ -1,6 +1,6 @@ import { type RefObject, React } from '@esengine/editor-runtime'; import { BehaviorTreeNode, Connection, ROOT_NODE_ID, useUIStore } from '../stores'; -import { PropertyDefinition } from '@esengine/behavior-tree'; +import { PropertyDefinition } from '../..'; import { useConnectionOperations } from './useConnectionOperations'; interface UsePortConnectionParams { diff --git a/packages/behavior-tree-editor/src/hooks/useQuickCreateMenu.ts b/packages/behavior-tree/src/editor/hooks/useQuickCreateMenu.ts similarity index 99% rename from packages/behavior-tree-editor/src/hooks/useQuickCreateMenu.ts rename to packages/behavior-tree/src/editor/hooks/useQuickCreateMenu.ts index f3403c5a..777de355 100644 --- a/packages/behavior-tree-editor/src/hooks/useQuickCreateMenu.ts +++ b/packages/behavior-tree/src/editor/hooks/useQuickCreateMenu.ts @@ -1,5 +1,5 @@ import { useState, type RefObject } from '@esengine/editor-runtime'; -import { NodeTemplate } from '@esengine/behavior-tree'; +import { NodeTemplate } from '../..'; import { BehaviorTreeNode, Connection, useBehaviorTreeDataStore } from '../stores'; import { Node } from '../domain/models/Node'; import { Position } from '../domain/value-objects/Position'; diff --git a/packages/behavior-tree/src/editor/index.ts b/packages/behavior-tree/src/editor/index.ts new file mode 100644 index 00000000..f4445cb3 --- /dev/null +++ b/packages/behavior-tree/src/editor/index.ts @@ -0,0 +1,287 @@ +/** + * Behavior Tree Editor Module + * 行为树编辑器模块 + */ + +import type { ServiceContainer } from '@esengine/ecs-framework'; +import { TransformComponent } from '@esengine/ecs-components'; +import { + type IEditorModuleLoader, + type IPluginLoader, + type PanelDescriptor, + type EntityCreationTemplate, + type FileCreationTemplate, + type FileActionHandler, + PanelPosition, + CompilerRegistry, + ICompilerRegistry, + InspectorRegistry, + IInspectorRegistry, + MessageHub, + IMessageHub, + createLogger, + PluginAPI, +} from '@esengine/editor-runtime'; + +// Runtime imports (relative paths since we're in the same package) +import { BehaviorTreeRuntimeComponent } from '../execution/BehaviorTreeRuntimeComponent'; + +// Editor components and services +import { BehaviorTreeService } from './services/BehaviorTreeService'; +import { FileSystemService } from './services/FileSystemService'; +import { BehaviorTreeCompiler } from './compiler/BehaviorTreeCompiler'; +import { BehaviorTreeNodeInspectorProvider } from './providers/BehaviorTreeNodeInspectorProvider'; +import { BehaviorTreeEditorPanel } from './components/panels/BehaviorTreeEditorPanel'; +import { useBehaviorTreeDataStore } from './stores'; +import { createRootNode } from './domain/constants/RootNode'; +import { PluginContext } from './PluginContext'; + +// Import runtime module and descriptor +import { BehaviorTreeRuntimeModule, descriptor } from './BehaviorTreePlugin'; + +// 导入编辑器 CSS 样式 +import './styles/BehaviorTreeNode.css'; +import './styles/Toast.css'; +import './components/panels/BehaviorTreeEditorPanel.css'; +import './components/panels/BehaviorTreePropertiesPanel.css'; + +const logger = createLogger('BehaviorTreeEditorModule'); + +/** + * Behavior Tree Editor Module + * 行为树编辑器模块加载器 + */ +export class BehaviorTreeEditorModule implements IEditorModuleLoader { + private services?: ServiceContainer; + + async install(services: ServiceContainer): Promise { + this.services = services; + + // 设置插件上下文 + PluginContext.setServices(services); + + // 注册服务 + this.registerServices(services); + + // 注册编译器 + this.registerCompilers(services); + + // 注册节点检视器 + this.registerInspectorProviders(services); + + logger.info('BehaviorTree editor module installed'); + } + + async uninstall(): Promise { + if (this.services) { + this.services.unregister(FileSystemService); + this.services.unregister(BehaviorTreeService); + } + + useBehaviorTreeDataStore.getState().reset(); + PluginContext.clear(); + this.services = undefined; + + logger.info('BehaviorTree editor module uninstalled'); + } + + private registerServices(services: ServiceContainer): void { + // FileSystemService (BehaviorTreeService depends on it) + if (services.isRegistered(FileSystemService)) { + services.unregister(FileSystemService); + } + services.registerSingleton(FileSystemService); + + // BehaviorTreeService + if (services.isRegistered(BehaviorTreeService)) { + services.unregister(BehaviorTreeService); + } + services.registerSingleton(BehaviorTreeService); + } + + private registerCompilers(services: ServiceContainer): void { + try { + const compilerRegistry = services.resolve(ICompilerRegistry); + const compiler = new BehaviorTreeCompiler(); + compilerRegistry.register(compiler); + logger.info('BehaviorTreeCompiler registered'); + } catch (error) { + logger.error('Failed to register compiler:', error); + } + } + + private registerInspectorProviders(services: ServiceContainer): void { + try { + const inspectorRegistry = services.resolve(IInspectorRegistry); + if (!inspectorRegistry) { + logger.error('InspectorRegistry not found in services'); + return; + } + + // 使用 Symbol 解析 MessageHub(跨包访问需要使用 Symbol) + const messageHub = services.resolve(IMessageHub); + if (!messageHub) { + logger.error('MessageHub not found in services'); + return; + } + + const provider = new BehaviorTreeNodeInspectorProvider(); + provider.setMessageHub(messageHub); + + inspectorRegistry.register(provider); + logger.info('BehaviorTreeNodeInspectorProvider registered'); + } catch (error) { + logger.error('Failed to register inspector provider:', error); + } + } + + getPanels(): PanelDescriptor[] { + return [ + { + id: 'behavior-tree-editor', + title: 'Behavior Tree Editor', + position: PanelPosition.Center, + closable: true, + component: BehaviorTreeEditorPanel, + order: 100, + isDynamic: true + } + ]; + } + + getFileCreationTemplates(): FileCreationTemplate[] { + return [{ + id: 'behavior-tree', + label: 'Behavior Tree', + extension: 'btree', + icon: 'GitBranch', + create: async (filePath: string) => { + const rootNode = createRootNode(); + const rootNodeData = { + id: rootNode.id, + type: rootNode.template.type, + displayName: rootNode.template.displayName, + data: rootNode.data, + position: { + x: rootNode.position.x, + y: rootNode.position.y + }, + children: [] + }; + + const emptyTree = { + name: filePath.replace(/.*[/\\]/, '').replace('.btree', ''), + nodes: [rootNodeData], + connections: [], + variables: {} + }; + + const content = JSON.stringify(emptyTree, null, 2); + // Write using Tauri FS API + const { writeTextFile } = await import('@tauri-apps/plugin-fs'); + await writeTextFile(filePath, content); + } + }]; + } + + getFileActionHandlers(): FileActionHandler[] { + return [{ + extensions: ['btree'], + onDoubleClick: async (filePath: string) => { + if (this.services) { + const service = this.services.resolve(BehaviorTreeService); + if (service) { + await service.loadFromFile(filePath); + } + } + } + }]; + } + + getEntityCreationTemplates(): EntityCreationTemplate[] { + return [{ + id: 'behavior-tree-entity', + label: 'AI Entity', + icon: 'GitBranch', + category: 'other', + order: 100, + create: (_parentEntityId?: number): number => { + const scene = PluginAPI.scene; + const entityStore = PluginAPI.entityStore; + const messageHub = PluginAPI.messageHub; + + // 统计现有 AI Entity 数量 + const aiEntityCount = entityStore.getAllEntities() + .filter((e: any) => e.name.startsWith('AI Entity')).length; + const entityName = `AI Entity ${aiEntityCount + 1}`; + + // 创建实体 + const entity = scene.createEntity(entityName); + + // 添加 Transform 组件 + entity.addComponent(new TransformComponent()); + + // 添加行为树运行时组件 + const btComponent = new BehaviorTreeRuntimeComponent(); + btComponent.autoStart = true; + entity.addComponent(btComponent); + + // 注册到实体存储 + entityStore.addEntity(entity); + + // 发送通知 + messageHub.publish('entity:added', { entity }); + messageHub.publish('scene:modified', {}); + + // 选中新创建的实体 + entityStore.selectEntity(entity); + + logger.info(`Created AI Entity: ${entity.id}`); + return entity.id; + } + }]; + } +} + +// Create the complete plugin with editor module +export const BehaviorTreePlugin: IPluginLoader = { + descriptor, + runtimeModule: new BehaviorTreeRuntimeModule(), + editorModule: new BehaviorTreeEditorModule(), +}; + +export { BehaviorTreeRuntimeModule }; + +// Re-exports for editor functionality +export { PluginContext } from './PluginContext'; +export { BehaviorTreeEditorPanel } from './components/panels/BehaviorTreeEditorPanel'; +export * from './services/BehaviorTreeService'; +export * from './providers/BehaviorTreeNodeInspectorProvider'; + +export * from './domain'; +export * from './application/commands/tree'; +export * from './application/use-cases'; +export * from './application/services/BlackboardManager'; +export * from './application/services/ExecutionController'; +export * from './application/services/GlobalBlackboardService'; +export * from './application/interfaces/IExecutionHooks'; +export * from './application/state/BehaviorTreeDataStore'; +export * from './hooks'; +export * from './stores'; +export type { EditorConfig } from './types'; +export * from './infrastructure/factories/NodeFactory'; +export * from './infrastructure/serialization/BehaviorTreeSerializer'; +export * from './infrastructure/validation/BehaviorTreeValidator'; +export * from './infrastructure/events/EditorEventBus'; +export * from './infrastructure/services/NodeRegistryService'; +export * from './utils/BehaviorTreeExecutor'; +export * from './utils/DOMCache'; +export * from './utils/portUtils'; +export * from './utils/RuntimeLoader'; +export * from './compiler/BehaviorTreeCompiler'; +export { + ICON_MAP, + ROOT_NODE_TEMPLATE, + DEFAULT_EDITOR_CONFIG +} from './config/editorConstants'; +export * from './interfaces/IEditorExtensions'; diff --git a/packages/behavior-tree-editor/src/infrastructure/events/EditorEventBus.ts b/packages/behavior-tree/src/editor/infrastructure/events/EditorEventBus.ts similarity index 100% rename from packages/behavior-tree-editor/src/infrastructure/events/EditorEventBus.ts rename to packages/behavior-tree/src/editor/infrastructure/events/EditorEventBus.ts diff --git a/packages/behavior-tree-editor/src/infrastructure/factories/NodeFactory.ts b/packages/behavior-tree/src/editor/infrastructure/factories/NodeFactory.ts similarity index 97% rename from packages/behavior-tree-editor/src/infrastructure/factories/NodeFactory.ts rename to packages/behavior-tree/src/editor/infrastructure/factories/NodeFactory.ts index f683d6b2..4bb9228c 100644 --- a/packages/behavior-tree-editor/src/infrastructure/factories/NodeFactory.ts +++ b/packages/behavior-tree/src/editor/infrastructure/factories/NodeFactory.ts @@ -1,4 +1,4 @@ -import { NodeTemplate, NodeTemplates } from '@esengine/behavior-tree'; +import { NodeTemplate, NodeTemplates } from '../../..'; import { Node } from '../../domain/models/Node'; import { Position } from '../../domain/value-objects/Position'; import { INodeFactory } from '../../domain/interfaces/INodeFactory'; diff --git a/packages/behavior-tree-editor/src/infrastructure/factories/index.ts b/packages/behavior-tree/src/editor/infrastructure/factories/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/infrastructure/factories/index.ts rename to packages/behavior-tree/src/editor/infrastructure/factories/index.ts diff --git a/packages/behavior-tree-editor/src/infrastructure/index.ts b/packages/behavior-tree/src/editor/infrastructure/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/infrastructure/index.ts rename to packages/behavior-tree/src/editor/infrastructure/index.ts diff --git a/packages/behavior-tree-editor/src/infrastructure/serialization/BehaviorTreeSerializer.ts b/packages/behavior-tree/src/editor/infrastructure/serialization/BehaviorTreeSerializer.ts similarity index 99% rename from packages/behavior-tree-editor/src/infrastructure/serialization/BehaviorTreeSerializer.ts rename to packages/behavior-tree/src/editor/infrastructure/serialization/BehaviorTreeSerializer.ts index 51f3d989..9fd868e0 100644 --- a/packages/behavior-tree-editor/src/infrastructure/serialization/BehaviorTreeSerializer.ts +++ b/packages/behavior-tree/src/editor/infrastructure/serialization/BehaviorTreeSerializer.ts @@ -1,6 +1,6 @@ import { BehaviorTree } from '../../domain/models/BehaviorTree'; import { ISerializer, SerializationFormat } from '../../domain/interfaces/ISerializer'; -import { BehaviorTreeAssetSerializer, EditorFormatConverter } from '@esengine/behavior-tree'; +import { BehaviorTreeAssetSerializer, EditorFormatConverter } from '../../..'; /** * 序列化选项 diff --git a/packages/behavior-tree-editor/src/infrastructure/serialization/index.ts b/packages/behavior-tree/src/editor/infrastructure/serialization/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/infrastructure/serialization/index.ts rename to packages/behavior-tree/src/editor/infrastructure/serialization/index.ts diff --git a/packages/behavior-tree-editor/src/infrastructure/services/NodeRegistryService.ts b/packages/behavior-tree/src/editor/infrastructure/services/NodeRegistryService.ts similarity index 99% rename from packages/behavior-tree-editor/src/infrastructure/services/NodeRegistryService.ts rename to packages/behavior-tree/src/editor/infrastructure/services/NodeRegistryService.ts index 6bbd9eac..e567f7a7 100644 --- a/packages/behavior-tree-editor/src/infrastructure/services/NodeRegistryService.ts +++ b/packages/behavior-tree/src/editor/infrastructure/services/NodeRegistryService.ts @@ -1,4 +1,4 @@ -import { NodeTemplate, NodeMetadataRegistry, NodeMetadata, NodeType } from '@esengine/behavior-tree'; +import { NodeTemplate, NodeMetadataRegistry, NodeMetadata, NodeType } from '../../..'; /** * 简化的节点注册配置 diff --git a/packages/behavior-tree-editor/src/infrastructure/validation/BehaviorTreeValidator.ts b/packages/behavior-tree/src/editor/infrastructure/validation/BehaviorTreeValidator.ts similarity index 100% rename from packages/behavior-tree-editor/src/infrastructure/validation/BehaviorTreeValidator.ts rename to packages/behavior-tree/src/editor/infrastructure/validation/BehaviorTreeValidator.ts diff --git a/packages/behavior-tree-editor/src/interfaces/IEditorExtensions.ts b/packages/behavior-tree/src/editor/interfaces/IEditorExtensions.ts similarity index 99% rename from packages/behavior-tree-editor/src/interfaces/IEditorExtensions.ts rename to packages/behavior-tree/src/editor/interfaces/IEditorExtensions.ts index 5c3f6151..0d1faa0a 100644 --- a/packages/behavior-tree-editor/src/interfaces/IEditorExtensions.ts +++ b/packages/behavior-tree/src/editor/interfaces/IEditorExtensions.ts @@ -1,6 +1,6 @@ import { React, createLogger } from '@esengine/editor-runtime'; import type { LucideIcon } from '@esengine/editor-runtime'; -import { NodeTemplate } from '@esengine/behavior-tree'; +import { NodeTemplate } from '../..'; import { Node as BehaviorTreeNode } from '../domain/models/Node'; const logger = createLogger('IEditorExtensions'); diff --git a/packages/behavior-tree-editor/src/providers/BehaviorTreeNodeInspectorProvider.tsx b/packages/behavior-tree/src/editor/providers/BehaviorTreeNodeInspectorProvider.tsx similarity index 98% rename from packages/behavior-tree-editor/src/providers/BehaviorTreeNodeInspectorProvider.tsx rename to packages/behavior-tree/src/editor/providers/BehaviorTreeNodeInspectorProvider.tsx index 0d8d98c6..6ae22f38 100644 --- a/packages/behavior-tree-editor/src/providers/BehaviorTreeNodeInspectorProvider.tsx +++ b/packages/behavior-tree/src/editor/providers/BehaviorTreeNodeInspectorProvider.tsx @@ -7,10 +7,10 @@ import { MessageHub, FieldEditorRegistry, type FieldEditorContext, - Core, + PluginAPI, } from '@esengine/editor-runtime'; import { Node as BehaviorTreeNode } from '../domain/models/Node'; -import { PropertyDefinition } from '@esengine/behavior-tree'; +import { PropertyDefinition } from '../..'; /** * 节点属性编辑器组件 @@ -29,7 +29,7 @@ const PropertyEditor: React.FC = ({ property, value, onChan const renderInput = () => { // 特殊处理 treeAssetId 字段使用 asset 编辑器 if (property.name === 'treeAssetId') { - const fieldRegistry = Core.services.resolve(FieldEditorRegistry); + const fieldRegistry = PluginAPI.resolve(FieldEditorRegistry); const assetEditor = fieldRegistry.getEditor('asset'); if (assetEditor) { @@ -52,7 +52,7 @@ const PropertyEditor: React.FC = ({ property, value, onChan // 检查是否有特定的字段编辑器类型 if (property.fieldEditor) { - const fieldRegistry = Core.services.resolve(FieldEditorRegistry); + const fieldRegistry = PluginAPI.resolve(FieldEditorRegistry); const editor = fieldRegistry.getEditor(property.fieldEditor.type); if (editor) { diff --git a/packages/behavior-tree-editor/src/services/BehaviorTreeService.ts b/packages/behavior-tree/src/editor/services/BehaviorTreeService.ts similarity index 100% rename from packages/behavior-tree-editor/src/services/BehaviorTreeService.ts rename to packages/behavior-tree/src/editor/services/BehaviorTreeService.ts diff --git a/packages/behavior-tree-editor/src/services/FileSystemService.ts b/packages/behavior-tree/src/editor/services/FileSystemService.ts similarity index 100% rename from packages/behavior-tree-editor/src/services/FileSystemService.ts rename to packages/behavior-tree/src/editor/services/FileSystemService.ts diff --git a/packages/behavior-tree-editor/src/services/NotificationService.ts b/packages/behavior-tree/src/editor/services/NotificationService.ts similarity index 67% rename from packages/behavior-tree-editor/src/services/NotificationService.ts rename to packages/behavior-tree/src/editor/services/NotificationService.ts index 46fa5b3f..b7ff61f8 100644 --- a/packages/behavior-tree-editor/src/services/NotificationService.ts +++ b/packages/behavior-tree/src/editor/services/NotificationService.ts @@ -1,4 +1,5 @@ -import { Core, createLogger, MessageHub } from '@esengine/editor-runtime'; +import { createLogger, PluginAPI } from '@esengine/editor-runtime'; +import type { MessageHub } from '@esengine/editor-runtime'; const logger = createLogger('NotificationService'); @@ -7,12 +8,18 @@ export class NotificationService { private messageHub: MessageHub | null = null; private constructor() { - // 尝试从 Core 获取 MessageHub - try { - this.messageHub = Core.services.resolve(MessageHub); - } catch (error) { - logger.warn('MessageHub not available, toast notifications will be disabled'); + // 延迟获取 MessageHub,因为初始化时可能还不可用 + } + + private getMessageHub(): MessageHub | null { + if (!this.messageHub && PluginAPI.isAvailable) { + try { + this.messageHub = PluginAPI.messageHub; + } catch (error) { + logger.warn('MessageHub not available'); + } } + return this.messageHub; } public static getInstance(): NotificationService { @@ -23,7 +30,8 @@ export class NotificationService { } public showToast(message: string, type: 'success' | 'error' | 'warning' | 'info' = 'info'): void { - if (!this.messageHub) { + const hub = this.getMessageHub(); + if (!hub) { logger.info(`[Toast ${type}] ${message}`); return; } @@ -34,7 +42,7 @@ export class NotificationService { timestamp: Date.now() }; - this.messageHub.publish('notification:show', notification); + hub.publish('notification:show', notification); } public success(message: string): void { diff --git a/packages/behavior-tree-editor/src/services/index.ts b/packages/behavior-tree/src/editor/services/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/services/index.ts rename to packages/behavior-tree/src/editor/services/index.ts diff --git a/packages/behavior-tree-editor/src/stores/ExecutionStatsStore.ts b/packages/behavior-tree/src/editor/stores/ExecutionStatsStore.ts similarity index 100% rename from packages/behavior-tree-editor/src/stores/ExecutionStatsStore.ts rename to packages/behavior-tree/src/editor/stores/ExecutionStatsStore.ts diff --git a/packages/behavior-tree-editor/src/stores/index.ts b/packages/behavior-tree/src/editor/stores/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/stores/index.ts rename to packages/behavior-tree/src/editor/stores/index.ts diff --git a/packages/behavior-tree-editor/src/stores/useUIStore.ts b/packages/behavior-tree/src/editor/stores/useUIStore.ts similarity index 100% rename from packages/behavior-tree-editor/src/stores/useUIStore.ts rename to packages/behavior-tree/src/editor/stores/useUIStore.ts diff --git a/packages/behavior-tree-editor/src/styles/BehaviorTreeNode.css b/packages/behavior-tree/src/editor/styles/BehaviorTreeNode.css similarity index 100% rename from packages/behavior-tree-editor/src/styles/BehaviorTreeNode.css rename to packages/behavior-tree/src/editor/styles/BehaviorTreeNode.css diff --git a/packages/behavior-tree-editor/src/styles/Toast.css b/packages/behavior-tree/src/editor/styles/Toast.css similarity index 100% rename from packages/behavior-tree-editor/src/styles/Toast.css rename to packages/behavior-tree/src/editor/styles/Toast.css diff --git a/packages/behavior-tree-editor/src/types/Breakpoint.ts b/packages/behavior-tree/src/editor/types/Breakpoint.ts similarity index 100% rename from packages/behavior-tree-editor/src/types/Breakpoint.ts rename to packages/behavior-tree/src/editor/types/Breakpoint.ts diff --git a/packages/behavior-tree-editor/src/types/index.ts b/packages/behavior-tree/src/editor/types/index.ts similarity index 100% rename from packages/behavior-tree-editor/src/types/index.ts rename to packages/behavior-tree/src/editor/types/index.ts diff --git a/packages/behavior-tree-editor/src/utils/BehaviorTreeExecutor.ts b/packages/behavior-tree/src/editor/utils/BehaviorTreeExecutor.ts similarity index 99% rename from packages/behavior-tree-editor/src/utils/BehaviorTreeExecutor.ts rename to packages/behavior-tree/src/editor/utils/BehaviorTreeExecutor.ts index e58dd8a0..ca7d59c7 100644 --- a/packages/behavior-tree-editor/src/utils/BehaviorTreeExecutor.ts +++ b/packages/behavior-tree/src/editor/utils/BehaviorTreeExecutor.ts @@ -7,7 +7,7 @@ import { BehaviorTreeExecutionSystem, TaskStatus, NodeType -} from '@esengine/behavior-tree'; +} from '../..'; import type { BehaviorTreeNode } from '../stores'; import { useExecutionStatsStore } from '../stores/ExecutionStatsStore'; import type { Breakpoint } from '../types/Breakpoint'; diff --git a/packages/behavior-tree-editor/src/utils/DOMCache.ts b/packages/behavior-tree/src/editor/utils/DOMCache.ts similarity index 100% rename from packages/behavior-tree-editor/src/utils/DOMCache.ts rename to packages/behavior-tree/src/editor/utils/DOMCache.ts diff --git a/packages/behavior-tree-editor/src/utils/RuntimeLoader.ts b/packages/behavior-tree/src/editor/utils/RuntimeLoader.ts similarity index 98% rename from packages/behavior-tree-editor/src/utils/RuntimeLoader.ts rename to packages/behavior-tree/src/editor/utils/RuntimeLoader.ts index e4f95614..2d096212 100644 --- a/packages/behavior-tree-editor/src/utils/RuntimeLoader.ts +++ b/packages/behavior-tree/src/editor/utils/RuntimeLoader.ts @@ -1,4 +1,4 @@ -import { BehaviorTreeAssetManager, BehaviorTreeData } from '@esengine/behavior-tree'; +import { BehaviorTreeAssetManager, BehaviorTreeData } from '../..'; import { BehaviorTreeSerializer } from '../infrastructure/serialization/BehaviorTreeSerializer'; import { BehaviorTree } from '../domain/models/BehaviorTree'; diff --git a/packages/behavior-tree-editor/src/utils/portUtils.ts b/packages/behavior-tree/src/editor/utils/portUtils.ts similarity index 100% rename from packages/behavior-tree-editor/src/utils/portUtils.ts rename to packages/behavior-tree/src/editor/utils/portUtils.ts diff --git a/packages/behavior-tree/src/Runtime/BehaviorTreeAssetManager.ts b/packages/behavior-tree/src/execution/BehaviorTreeAssetManager.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/BehaviorTreeAssetManager.ts rename to packages/behavior-tree/src/execution/BehaviorTreeAssetManager.ts diff --git a/packages/behavior-tree/src/Runtime/BehaviorTreeData.ts b/packages/behavior-tree/src/execution/BehaviorTreeData.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/BehaviorTreeData.ts rename to packages/behavior-tree/src/execution/BehaviorTreeData.ts diff --git a/packages/behavior-tree/src/Runtime/BehaviorTreeExecutionSystem.ts b/packages/behavior-tree/src/execution/BehaviorTreeExecutionSystem.ts similarity index 92% rename from packages/behavior-tree/src/Runtime/BehaviorTreeExecutionSystem.ts rename to packages/behavior-tree/src/execution/BehaviorTreeExecutionSystem.ts index 2f5e5ce9..7969dda4 100644 --- a/packages/behavior-tree/src/Runtime/BehaviorTreeExecutionSystem.ts +++ b/packages/behavior-tree/src/execution/BehaviorTreeExecutionSystem.ts @@ -14,16 +14,25 @@ import './Executors'; */ @ECSSystem('BehaviorTreeExecution') export class BehaviorTreeExecutionSystem extends EntitySystem { - private assetManager: BehaviorTreeAssetManager; + private assetManager: BehaviorTreeAssetManager | null = null; private executorRegistry: NodeExecutorRegistry; + private coreInstance: typeof Core | null = null; - constructor() { + constructor(coreInstance?: typeof Core) { super(Matcher.empty().all(BehaviorTreeRuntimeComponent)); - this.assetManager = Core.services.resolve(BehaviorTreeAssetManager); + this.coreInstance = coreInstance || null; this.executorRegistry = new NodeExecutorRegistry(); this.registerBuiltInExecutors(); } + private getAssetManager(): BehaviorTreeAssetManager { + if (!this.assetManager) { + const core = this.coreInstance || Core; + this.assetManager = core.services.resolve(BehaviorTreeAssetManager); + } + return this.assetManager; + } + /** * 注册所有执行器(包括内置和插件提供的) */ @@ -55,7 +64,7 @@ export class BehaviorTreeExecutionSystem extends EntitySystem { continue; } - const treeData = this.assetManager.getAsset(runtime.treeAssetId); + const treeData = this.getAssetManager().getAsset(runtime.treeAssetId); if (!treeData) { this.logger.warn(`未找到行为树资产: ${runtime.treeAssetId}`); continue; diff --git a/packages/behavior-tree/src/Runtime/BehaviorTreeRuntimeComponent.ts b/packages/behavior-tree/src/execution/BehaviorTreeRuntimeComponent.ts similarity index 96% rename from packages/behavior-tree/src/Runtime/BehaviorTreeRuntimeComponent.ts rename to packages/behavior-tree/src/execution/BehaviorTreeRuntimeComponent.ts index e6988235..b2d26589 100644 --- a/packages/behavior-tree/src/Runtime/BehaviorTreeRuntimeComponent.ts +++ b/packages/behavior-tree/src/execution/BehaviorTreeRuntimeComponent.ts @@ -1,4 +1,4 @@ -import { Component, ECSComponent } from '@esengine/ecs-framework'; +import { Component, ECSComponent, Property } from '@esengine/ecs-framework'; import { Serializable, Serialize, IgnoreSerialization } from '@esengine/ecs-framework'; import { NodeRuntimeState, createDefaultRuntimeState } from './BehaviorTreeData'; import { TaskStatus } from '../Types/TaskStatus'; @@ -30,12 +30,14 @@ export class BehaviorTreeRuntimeComponent extends Component { * 引用的行为树资产ID(可序列化) */ @Serialize() + @Property({ type: 'asset', label: 'Behavior Tree', extensions: ['.btree'] }) treeAssetId: string = ''; /** * 是否自动启动 */ @Serialize() + @Property({ type: 'boolean', label: 'Auto Start' }) autoStart: boolean = true; /** diff --git a/packages/behavior-tree/src/Runtime/Executors/AlwaysFailExecutor.ts b/packages/behavior-tree/src/execution/Executors/AlwaysFailExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/AlwaysFailExecutor.ts rename to packages/behavior-tree/src/execution/Executors/AlwaysFailExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/AlwaysSucceedExecutor.ts b/packages/behavior-tree/src/execution/Executors/AlwaysSucceedExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/AlwaysSucceedExecutor.ts rename to packages/behavior-tree/src/execution/Executors/AlwaysSucceedExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/BlackboardCompare.ts b/packages/behavior-tree/src/execution/Executors/BlackboardCompare.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/BlackboardCompare.ts rename to packages/behavior-tree/src/execution/Executors/BlackboardCompare.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/BlackboardExists.ts b/packages/behavior-tree/src/execution/Executors/BlackboardExists.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/BlackboardExists.ts rename to packages/behavior-tree/src/execution/Executors/BlackboardExists.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/ConditionalExecutor.ts b/packages/behavior-tree/src/execution/Executors/ConditionalExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/ConditionalExecutor.ts rename to packages/behavior-tree/src/execution/Executors/ConditionalExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/CooldownExecutor.ts b/packages/behavior-tree/src/execution/Executors/CooldownExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/CooldownExecutor.ts rename to packages/behavior-tree/src/execution/Executors/CooldownExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/ExecuteAction.ts b/packages/behavior-tree/src/execution/Executors/ExecuteAction.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/ExecuteAction.ts rename to packages/behavior-tree/src/execution/Executors/ExecuteAction.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/ExecuteCondition.ts b/packages/behavior-tree/src/execution/Executors/ExecuteCondition.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/ExecuteCondition.ts rename to packages/behavior-tree/src/execution/Executors/ExecuteCondition.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/InverterExecutor.ts b/packages/behavior-tree/src/execution/Executors/InverterExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/InverterExecutor.ts rename to packages/behavior-tree/src/execution/Executors/InverterExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/LogAction.ts b/packages/behavior-tree/src/execution/Executors/LogAction.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/LogAction.ts rename to packages/behavior-tree/src/execution/Executors/LogAction.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/ModifyBlackboardValue.ts b/packages/behavior-tree/src/execution/Executors/ModifyBlackboardValue.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/ModifyBlackboardValue.ts rename to packages/behavior-tree/src/execution/Executors/ModifyBlackboardValue.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/ParallelExecutor.ts b/packages/behavior-tree/src/execution/Executors/ParallelExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/ParallelExecutor.ts rename to packages/behavior-tree/src/execution/Executors/ParallelExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/ParallelSelectorExecutor.ts b/packages/behavior-tree/src/execution/Executors/ParallelSelectorExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/ParallelSelectorExecutor.ts rename to packages/behavior-tree/src/execution/Executors/ParallelSelectorExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/RandomProbability.ts b/packages/behavior-tree/src/execution/Executors/RandomProbability.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/RandomProbability.ts rename to packages/behavior-tree/src/execution/Executors/RandomProbability.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/RandomSelectorExecutor.ts b/packages/behavior-tree/src/execution/Executors/RandomSelectorExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/RandomSelectorExecutor.ts rename to packages/behavior-tree/src/execution/Executors/RandomSelectorExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/RandomSequenceExecutor.ts b/packages/behavior-tree/src/execution/Executors/RandomSequenceExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/RandomSequenceExecutor.ts rename to packages/behavior-tree/src/execution/Executors/RandomSequenceExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/RepeaterExecutor.ts b/packages/behavior-tree/src/execution/Executors/RepeaterExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/RepeaterExecutor.ts rename to packages/behavior-tree/src/execution/Executors/RepeaterExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/SelectorExecutor.ts b/packages/behavior-tree/src/execution/Executors/SelectorExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/SelectorExecutor.ts rename to packages/behavior-tree/src/execution/Executors/SelectorExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/SequenceExecutor.ts b/packages/behavior-tree/src/execution/Executors/SequenceExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/SequenceExecutor.ts rename to packages/behavior-tree/src/execution/Executors/SequenceExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/ServiceDecorator.ts b/packages/behavior-tree/src/execution/Executors/ServiceDecorator.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/ServiceDecorator.ts rename to packages/behavior-tree/src/execution/Executors/ServiceDecorator.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/SetBlackboardValue.ts b/packages/behavior-tree/src/execution/Executors/SetBlackboardValue.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/SetBlackboardValue.ts rename to packages/behavior-tree/src/execution/Executors/SetBlackboardValue.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/SubTreeExecutor.ts b/packages/behavior-tree/src/execution/Executors/SubTreeExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/SubTreeExecutor.ts rename to packages/behavior-tree/src/execution/Executors/SubTreeExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/TimeoutExecutor.ts b/packages/behavior-tree/src/execution/Executors/TimeoutExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/TimeoutExecutor.ts rename to packages/behavior-tree/src/execution/Executors/TimeoutExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/UntilFailExecutor.ts b/packages/behavior-tree/src/execution/Executors/UntilFailExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/UntilFailExecutor.ts rename to packages/behavior-tree/src/execution/Executors/UntilFailExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/UntilSuccessExecutor.ts b/packages/behavior-tree/src/execution/Executors/UntilSuccessExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/UntilSuccessExecutor.ts rename to packages/behavior-tree/src/execution/Executors/UntilSuccessExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/WaitAction.ts b/packages/behavior-tree/src/execution/Executors/WaitAction.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/WaitAction.ts rename to packages/behavior-tree/src/execution/Executors/WaitAction.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/WaitActionExecutor.ts b/packages/behavior-tree/src/execution/Executors/WaitActionExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/WaitActionExecutor.ts rename to packages/behavior-tree/src/execution/Executors/WaitActionExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/Executors/index.ts b/packages/behavior-tree/src/execution/Executors/index.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/Executors/index.ts rename to packages/behavior-tree/src/execution/Executors/index.ts diff --git a/packages/behavior-tree/src/Runtime/NodeExecutor.ts b/packages/behavior-tree/src/execution/NodeExecutor.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/NodeExecutor.ts rename to packages/behavior-tree/src/execution/NodeExecutor.ts diff --git a/packages/behavior-tree/src/Runtime/NodeMetadata.ts b/packages/behavior-tree/src/execution/NodeMetadata.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/NodeMetadata.ts rename to packages/behavior-tree/src/execution/NodeMetadata.ts diff --git a/packages/behavior-tree/src/Runtime/index.ts b/packages/behavior-tree/src/execution/index.ts similarity index 100% rename from packages/behavior-tree/src/Runtime/index.ts rename to packages/behavior-tree/src/execution/index.ts diff --git a/packages/behavior-tree/src/index.ts b/packages/behavior-tree/src/index.ts index 52a0807f..36697633 100644 --- a/packages/behavior-tree/src/index.ts +++ b/packages/behavior-tree/src/index.ts @@ -1,30 +1,38 @@ /** * @esengine/behavior-tree * - * 行为树系统 + * AI Behavior Tree System with runtime execution and visual editor support + * AI 行为树系统,支持运行时执行和可视化编辑 * * @packageDocumentation */ -// 类型定义 +// Types export * from './Types/TaskStatus'; -// Runtime -export * from './Runtime'; +// Execution (runtime core) +export * from './execution'; -// 辅助工具 +// Utilities export * from './BehaviorTreeStarter'; export * from './BehaviorTreeBuilder'; -// 序列化 +// Serialization export * from './Serialization/NodeTemplates'; export * from './Serialization/BehaviorTreeAsset'; export * from './Serialization/EditorFormatConverter'; export * from './Serialization/BehaviorTreeAssetSerializer'; export * from './Serialization/EditorToBehaviorTreeDataConverter'; -// 服务 +// Services export * from './Services/GlobalBlackboardService'; -// 插件 -export * from './BehaviorTreePlugin'; +// Blackboard types (excluding BlackboardValueType which is already exported from TaskStatus) +export type { BlackboardTypeDefinition } from './Blackboard/BlackboardTypes'; +export { BlackboardTypes } from './Blackboard/BlackboardTypes'; + +// Runtime module (no editor dependencies) +export { BehaviorTreeRuntimeModule } from './BehaviorTreeRuntimeModule'; + +// Plugin (for PluginManager - includes editor dependencies) +export { BehaviorTreePlugin } from './editor/index'; diff --git a/packages/behavior-tree/src/runtime.ts b/packages/behavior-tree/src/runtime.ts new file mode 100644 index 00000000..8f89ad7f --- /dev/null +++ b/packages/behavior-tree/src/runtime.ts @@ -0,0 +1,36 @@ +/** + * @esengine/behavior-tree Runtime Entry Point + * + * This entry point exports only runtime-related code without any editor dependencies. + * Use this for standalone game runtime builds. + * + * 此入口点仅导出运行时相关代码,不包含任何编辑器依赖。 + * 用于独立游戏运行时构建。 + */ + +// Types +export * from './Types/TaskStatus'; + +// Execution (runtime core) +export * from './execution'; + +// Utilities +export * from './BehaviorTreeStarter'; +export * from './BehaviorTreeBuilder'; + +// Serialization +export * from './Serialization/NodeTemplates'; +export * from './Serialization/BehaviorTreeAsset'; +export * from './Serialization/EditorFormatConverter'; +export * from './Serialization/BehaviorTreeAssetSerializer'; +export * from './Serialization/EditorToBehaviorTreeDataConverter'; + +// Services +export * from './Services/GlobalBlackboardService'; + +// Blackboard types (excluding BlackboardValueType which is already exported from TaskStatus) +export type { BlackboardTypeDefinition } from './Blackboard/BlackboardTypes'; +export { BlackboardTypes } from './Blackboard/BlackboardTypes'; + +// Runtime module +export { BehaviorTreeRuntimeModule } from './BehaviorTreeRuntimeModule'; diff --git a/packages/behavior-tree/tsconfig.json b/packages/behavior-tree/tsconfig.json index ff4273da..cbca93a9 100644 --- a/packages/behavior-tree/tsconfig.json +++ b/packages/behavior-tree/tsconfig.json @@ -1,21 +1,38 @@ { - "extends": "../core/tsconfig.json", "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "bundler", + "allowImportingTsExtensions": false, + "lib": ["ES2020", "DOM"], + "outDir": "./dist", "rootDir": "./src", - "outDir": "./bin" + "strict": true, + "composite": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "jsx": "react-jsx", + "resolveJsonModule": true }, - "include": [ - "src/**/*" - ], + "include": ["src/**/*", "plugin.json"], "exclude": [ "node_modules", - "bin", + "dist", "**/*.test.ts", "**/*.spec.ts" ], "references": [ - { - "path": "../core" - } + { "path": "../core" }, + { "path": "../components" }, + { "path": "../editor-core" }, + { "path": "../ui" }, + { "path": "../editor-runtime" } ] } diff --git a/packages/behavior-tree/vite.config.ts b/packages/behavior-tree/vite.config.ts index ed03c23b..131633cd 100644 --- a/packages/behavior-tree/vite.config.ts +++ b/packages/behavior-tree/vite.config.ts @@ -1,20 +1,95 @@ import { defineConfig } from 'vite'; import { resolve } from 'path'; +import dts from 'vite-plugin-dts'; +import react from '@vitejs/plugin-react'; + +// 自定义插件:将 CSS 内联到 JS 中 +function inlineCSS(): any { + return { + name: 'inline-css', + enforce: 'post' as const, + // 在生成 bundle 时注入 CSS + generateBundle(_options: any, bundle: any) { + const bundleKeys = Object.keys(bundle); + + // 找到 CSS 文件 + const cssFile = bundleKeys.find(key => key.endsWith('.css')); + if (!cssFile || !bundle[cssFile]) { + return; + } + + const cssContent = bundle[cssFile].source; + if (!cssContent) return; + + // 找到包含编辑器代码的主要 JS 文件(带 hash 的 chunk) + const mainJsFile = bundleKeys.find(key => + key.endsWith('.js') && + key.includes('index-') && + bundle[key].type === 'chunk' && + bundle[key].code + ); + + if (mainJsFile && bundle[mainJsFile]) { + const injectCode = ` +(function() { + if (typeof document !== 'undefined') { + var style = document.createElement('style'); + style.id = 'esengine-behavior-tree-styles'; + if (!document.getElementById(style.id)) { + style.textContent = ${JSON.stringify(cssContent)}; + document.head.appendChild(style); + } + } +})(); +`; + bundle[mainJsFile].code = injectCode + bundle[mainJsFile].code; + } + + // 删除独立的 CSS 文件(已内联) + delete bundle[cssFile]; + } + }; +} export default defineConfig({ + plugins: [ + react(), + dts({ + include: ['src'], + outDir: 'dist', + rollupTypes: false + }), + inlineCSS() + ], + esbuild: { + jsx: 'automatic', + }, build: { lib: { - entry: resolve(__dirname, 'src/index.ts'), + entry: { + index: resolve(__dirname, 'src/index.ts'), + runtime: resolve(__dirname, 'src/runtime.ts'), + 'editor/index': resolve(__dirname, 'src/editor/index.ts') + }, formats: ['es'], - fileName: () => 'behavior-tree.js' + fileName: (format, entryName) => `${entryName}.js` }, rollupOptions: { + external: [ + '@esengine/ecs-framework', + '@esengine/editor-runtime', + 'react', + 'react/jsx-runtime', + 'lucide-react', + 'zustand', + /^@esengine\//, + /^@tauri-apps\// + ], output: { exports: 'named', - inlineDynamicImports: true + preserveModules: false } }, - outDir: 'dist', target: 'es2020', minify: false, sourcemap: true diff --git a/packages/components/package.json b/packages/components/package.json index d7d801a0..b8736c16 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -12,10 +12,12 @@ "types": "./src/index.ts", "import": "./src/index.ts" } - } + }, + "./plugin.json": "./plugin.json" }, "files": [ - "bin/**/*" + "bin/**/*", + "plugin.json" ], "keywords": [ "ecs", diff --git a/packages/components/plugin.json b/packages/components/plugin.json new file mode 100644 index 00000000..cb4b79d5 --- /dev/null +++ b/packages/components/plugin.json @@ -0,0 +1,20 @@ +{ + "id": "@esengine/ecs-components", + "name": "Core Components", + "version": "1.0.0", + "description": "Transform, Sprite, Camera 等核心组件", + "category": "core", + "loadingPhase": "preDefault", + "enabledByDefault": true, + "canContainContent": false, + "isEnginePlugin": true, + "modules": [ + { + "name": "CoreRuntime", + "type": "runtime", + "entry": "./src/index.ts" + } + ], + "dependencies": [], + "icon": "Settings" +} diff --git a/packages/components/src/CorePlugin.ts b/packages/components/src/CorePlugin.ts new file mode 100644 index 00000000..3591b80d --- /dev/null +++ b/packages/components/src/CorePlugin.ts @@ -0,0 +1,130 @@ +/** + * Core Components Plugin + * 核心组件插件 + * + * 提供基础的 Transform、Sprite、Camera 等核心组件 + * 这是一个核心插件,不可禁用 + */ + +import type { ComponentRegistry as ComponentRegistryType, IScene, ServiceContainer } from '@esengine/ecs-framework'; + +// Components +import { TransformComponent } from './TransformComponent'; +import { SpriteComponent } from './SpriteComponent'; +import { SpriteAnimatorComponent } from './SpriteAnimatorComponent'; +import { CameraComponent } from './CameraComponent'; + +// Systems +import { SpriteAnimatorSystem } from './systems/SpriteAnimatorSystem'; + + +/** + * 系统创建上下文 + */ +export interface SystemContext { + isEditor: boolean; + engineBridge?: any; + renderSystem?: any; + [key: string]: any; +} + +/** + * 插件描述符类型 + */ +export interface PluginDescriptor { + id: string; + name: string; + version: string; + description?: string; + category?: string; + loadingPhase?: string; + enabledByDefault?: boolean; + canContainContent?: boolean; + isEnginePlugin?: boolean; + modules?: Array<{ + name: string; + type: string; + entry: string; + }>; + dependencies?: Array<{ + id: string; + version?: string; + }>; + icon?: string; +} + +/** + * 运行时模块加载器接口 + */ +export interface IRuntimeModuleLoader { + registerComponents(registry: typeof ComponentRegistryType): void; + registerServices?(services: ServiceContainer): void; + createSystems?(scene: IScene, context: SystemContext): void; + onInitialize?(): Promise; + onDestroy?(): void; +} + +/** + * 插件加载器接口 + */ +export interface IPluginLoader { + readonly descriptor: PluginDescriptor; + readonly runtimeModule?: IRuntimeModuleLoader; + readonly editorModule?: any; +} + +/** + * 核心组件运行时模块 + */ +export class CoreRuntimeModule implements IRuntimeModuleLoader { + registerComponents(registry: typeof ComponentRegistryType): void { + registry.register(TransformComponent); + registry.register(SpriteComponent); + registry.register(SpriteAnimatorComponent); + registry.register(CameraComponent); + } + + createSystems(scene: IScene, context: SystemContext): void { + const animatorSystem = new SpriteAnimatorSystem(); + + if (context.isEditor) { + animatorSystem.enabled = false; + } + + scene.addSystem(animatorSystem); + context.animatorSystem = animatorSystem; + } +} + +/** + * 插件描述符 + */ +const descriptor: PluginDescriptor = { + id: '@esengine/ecs-components', + name: 'Core Components', + version: '1.0.0', + description: 'Transform, Sprite, Camera 等核心组件', + category: 'core', + loadingPhase: 'preDefault', + enabledByDefault: true, + canContainContent: false, + isEnginePlugin: true, + modules: [ + { + name: 'CoreRuntime', + type: 'runtime', + entry: './src/index.ts' + } + ], + icon: 'Settings' +}; + +/** + * 核心组件插件 + */ +export const CorePlugin: IPluginLoader = { + descriptor, + runtimeModule: new CoreRuntimeModule(), +}; + +export default CorePlugin; diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 9d1cdf93..8a7537e0 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -16,4 +16,8 @@ export { BoxColliderComponent } from './BoxColliderComponent'; export { CircleColliderComponent } from './CircleColliderComponent'; // 音频 -export { AudioSourceComponent } from './AudioSourceComponent'; \ No newline at end of file +export { AudioSourceComponent } from './AudioSourceComponent'; + +// Plugin (unified plugin system) +export { CorePlugin, CoreRuntimeModule } from './CorePlugin'; +export type { SystemContext, PluginDescriptor, IRuntimeModuleLoader, IPluginLoader } from './CorePlugin'; \ No newline at end of file diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index 41681a83..27a8ccf8 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -35,7 +35,8 @@ "resolveJsonModule": true }, "include": [ - "src/**/*" + "src/**/*", + "plugin.json" ], "exclude": [ "node_modules", diff --git a/packages/core/src/ECS/Decorators/PropertyDecorator.ts b/packages/core/src/ECS/Decorators/PropertyDecorator.ts index 1150b373..85437bd0 100644 --- a/packages/core/src/ECS/Decorators/PropertyDecorator.ts +++ b/packages/core/src/ECS/Decorators/PropertyDecorator.ts @@ -146,7 +146,9 @@ export type PropertyOptions = | AssetPropertyOptions | AnimationClipsPropertyOptions; -export const PROPERTY_METADATA = Symbol('property:metadata'); +// 使用 Symbol.for 创建全局 Symbol,确保跨包共享元数据 +// Use Symbol.for to create a global Symbol to ensure metadata sharing across packages +export const PROPERTY_METADATA = Symbol.for('@esengine/property:metadata'); /** * 属性装饰器 - 声明组件属性的编辑器元数据 diff --git a/packages/core/src/ECS/Systems/EntitySystem.ts b/packages/core/src/ECS/Systems/EntitySystem.ts index a1946428..ba8f6e16 100644 --- a/packages/core/src/ECS/Systems/EntitySystem.ts +++ b/packages/core/src/ECS/Systems/EntitySystem.ts @@ -284,6 +284,11 @@ export abstract class EntitySystem implements ISystemBase, IService { const querySystem = this.scene.querySystem; let currentEntities: readonly Entity[] = []; + // matchNothing 条件返回空数组(用于只需要生命周期方法的系统) + if (this._matcher.isNothing()) { + return []; + } + // 空条件返回所有实体 if (this._matcher.isEmpty()) { currentEntities = querySystem.getAllEntities(); diff --git a/packages/core/src/ECS/Utils/Matcher.ts b/packages/core/src/ECS/Utils/Matcher.ts index 31e97580..ace58fb2 100644 --- a/packages/core/src/ECS/Utils/Matcher.ts +++ b/packages/core/src/ECS/Utils/Matcher.ts @@ -11,6 +11,7 @@ export interface QueryCondition { tag?: number; // 按标签查询 name?: string; // 按名称查询 component?: ComponentType; // 单组件查询 + matchNothing?: boolean; // 不匹配任何实体 } /** @@ -107,6 +108,34 @@ export class Matcher { return new Matcher(); } + /** + * 创建不匹配任何实体的匹配器 + * 用于只需要 onBegin/onEnd 生命周期方法但不需要处理实体的系统 + * + * @example + * ```typescript + * // 创建一个只在帧开始时执行的系统 + * class FrameBeginSystem extends EntitySystem { + * constructor() { + * super(Matcher.nothing()); + * } + * + * protected onBegin(): void { + * // 每帧开始时执行 + * } + * + * protected process(entities: readonly Entity[]): void { + * // 永远不会被调用,因为没有实体匹配 + * } + * } + * ``` + */ + public static nothing(): Matcher { + const matcher = new Matcher(); + matcher.condition.matchNothing = true; + return matcher; + } + /** * 必须包含所有指定组件 * @param types 组件类型 @@ -211,12 +240,14 @@ export class Matcher { none: [...this.condition.none], ...(this.condition.tag !== undefined && { tag: this.condition.tag }), ...(this.condition.name !== undefined && { name: this.condition.name }), - ...(this.condition.component !== undefined && { component: this.condition.component }) + ...(this.condition.component !== undefined && { component: this.condition.component }), + ...(this.condition.matchNothing && { matchNothing: true }) }; } /** * 检查是否为空条件 + * 注意:matchNothing 不算空条件,因为它是明确的"不匹配任何实体"语义 */ public isEmpty(): boolean { return this.condition.all.length === 0 && @@ -224,7 +255,15 @@ export class Matcher { this.condition.none.length === 0 && this.condition.tag === undefined && this.condition.name === undefined && - this.condition.component === undefined; + this.condition.component === undefined && + !this.condition.matchNothing; + } + + /** + * 检查是否为"不匹配任何实体"的匹配器 + */ + public isNothing(): boolean { + return this.condition.matchNothing === true; } /** @@ -237,6 +276,7 @@ export class Matcher { delete this.condition.tag; delete this.condition.name; delete this.condition.component; + delete this.condition.matchNothing; return this; } @@ -257,6 +297,9 @@ export class Matcher { if (this.condition.component !== undefined) { cloned.condition.component = this.condition.component; } + if (this.condition.matchNothing) { + cloned.condition.matchNothing = true; + } return cloned; } @@ -264,6 +307,10 @@ export class Matcher { * 字符串表示 */ public toString(): string { + if (this.condition.matchNothing) { + return 'Matcher[nothing]'; + } + const parts: string[] = []; if (this.condition.all.length > 0) { diff --git a/packages/core/tests/ECS/Systems/EntitySystem.test.ts b/packages/core/tests/ECS/Systems/EntitySystem.test.ts index a26e6673..91daed01 100644 --- a/packages/core/tests/ECS/Systems/EntitySystem.test.ts +++ b/packages/core/tests/ECS/Systems/EntitySystem.test.ts @@ -266,4 +266,358 @@ describe('EntitySystem', () => { }); }); + describe('nothing 匹配器与系统', () => { + class NothingSystem extends EntitySystem { + public onBeginCallCount = 0; + public processCallCount = 0; + public onEndCallCount = 0; + + constructor() { + super(Matcher.nothing()); + } + + protected override onBegin(): void { + this.onBeginCallCount++; + } + + protected override process(entities: readonly Entity[]): void { + this.processCallCount++; + // nothing 匹配器不应该有任何实体 + expect(entities).toHaveLength(0); + } + + protected override onEnd(): void { + this.onEndCallCount++; + } + } + + it('使用 nothing 匹配器的系统应该正常运行生命周期方法', () => { + const nothingSystem = new NothingSystem(); + scene.addSystem(nothingSystem); + + // 手动触发更新 + nothingSystem.update(); + nothingSystem.lateUpdate(); + + // 生命周期方法应该被调用 + expect(nothingSystem.onBeginCallCount).toBe(1); + expect(nothingSystem.processCallCount).toBe(1); + expect(nothingSystem.onEndCallCount).toBe(1); + + // 清理 + scene.removeSystem(nothingSystem); + }); + + it('nothing 匹配器系统的 entities 应该为空', () => { + const nothingSystem = new NothingSystem(); + scene.addSystem(nothingSystem); + + // 即使场景中有实体,entities 也应该为空 + expect(nothingSystem.entities).toHaveLength(0); + + scene.removeSystem(nothingSystem); + }); + }); + + describe('系统更新顺序', () => { + it('setUpdateOrder 应该能够设置更新顺序', () => { + const plainSystem = new PlainEntitySystem(); + scene.addSystem(plainSystem); + + expect(plainSystem.updateOrder).toBe(0); + + plainSystem.setUpdateOrder(10); + expect(plainSystem.updateOrder).toBe(10); + + plainSystem.updateOrder = 20; + expect(plainSystem.updateOrder).toBe(20); + + scene.removeSystem(plainSystem); + }); + + it('设置相同的 updateOrder 不应该触发重排序', () => { + const plainSystem = new PlainEntitySystem(); + scene.addSystem(plainSystem); + + plainSystem.setUpdateOrder(10); + // 设置相同的值不应该有问题 + plainSystem.setUpdateOrder(10); + + expect(plainSystem.updateOrder).toBe(10); + + scene.removeSystem(plainSystem); + }); + }); + + describe('系统启用/禁用', () => { + it('禁用系统时不应该调用 process', () => { + const plainSystem = new PlainEntitySystem(); + scene.addSystem(plainSystem); + + plainSystem.enabled = false; + plainSystem.update(); + + expect(plainSystem.processCallCount).toBe(0); + + scene.removeSystem(plainSystem); + }); + + it('重新启用系统后应该正常调用 process', () => { + const plainSystem = new PlainEntitySystem(); + scene.addSystem(plainSystem); + + plainSystem.enabled = false; + plainSystem.update(); + expect(plainSystem.processCallCount).toBe(0); + + plainSystem.enabled = true; + plainSystem.update(); + expect(plainSystem.processCallCount).toBe(1); + + scene.removeSystem(plainSystem); + }); + }); + + describe('实体跟踪回调', () => { + class TrackingSystem extends EntitySystem { + public addedEntities: Entity[] = []; + public removedEntities: Entity[] = []; + + constructor() { + super(Matcher.all(TestComponent)); + } + + protected override process(entities: readonly Entity[]): void { + // 处理实体 + } + + protected override onAdded(entity: Entity): void { + this.addedEntities.push(entity); + } + + protected override onRemoved(entity: Entity): void { + this.removedEntities.push(entity); + } + } + + it('当实体添加组件后应该触发 onAdded', () => { + const trackingSystem = new TrackingSystem(); + scene.addSystem(trackingSystem); + + // 创建新实体并添加组件 + const newEntity = scene.createEntity('new_entity'); + newEntity.addComponent(new TestComponent(5)); + scene.addEntity(newEntity); + + // 触发更新以检测变化 + trackingSystem.update(); + + // 应该检测到新实体 + expect(trackingSystem.addedEntities.length).toBeGreaterThan(0); + + scene.removeSystem(trackingSystem); + }); + + it('当实体移除组件后应该触发 onRemoved', () => { + const trackingSystem = new TrackingSystem(); + scene.addSystem(trackingSystem); + + // 先触发更新来跟踪现有实体 + trackingSystem.update(); + + // 移除组件 + const comp = entity.getComponent(TestComponent); + if (comp) { + entity.removeComponent(comp); + } + + // 再次更新 + trackingSystem.update(); + + // 应该检测到实体被移除 + expect(trackingSystem.removedEntities.length).toBeGreaterThan(0); + + scene.removeSystem(trackingSystem); + }); + }); + + describe('reset 方法', () => { + it('reset 后系统应该可以重新初始化', () => { + const plainSystem = new PlainEntitySystem(); + scene.addSystem(plainSystem); + + // 调用 reset + plainSystem.reset(); + + // 验证系统被重置 + expect(plainSystem.scene).toBeNull(); + }); + + it('已销毁的系统调用 reset 不应该执行任何操作', () => { + const plainSystem = new PlainEntitySystem(); + scene.addSystem(plainSystem); + + // 先销毁系统 + plainSystem.destroy(); + + // reset 不应该有任何效果 + plainSystem.reset(); + + // onDestroy 只应被调用一次 + expect(plainSystem.onDestroyCallCount).toBe(1); + }); + }); + + describe('辅助方法', () => { + class HelperSystem extends EntitySystem { + constructor() { + super(Matcher.all(TestComponent)); + } + + protected override process(entities: readonly Entity[]): void {} + + // 暴露 protected 方法供测试 + public testRequireComponent Component>( + entity: Entity, + componentType: T + ): InstanceType { + return this.requireComponent(entity, componentType) as InstanceType; + } + + public testForEach( + entities: readonly Entity[], + processor: (entity: Entity, index: number) => void + ): void { + this.forEach(entities, processor); + } + + public testFilterEntities( + entities: readonly Entity[], + predicate: (entity: Entity, index: number) => boolean + ): Entity[] { + return this.filterEntities(entities, predicate); + } + + public testFindEntity( + entities: readonly Entity[], + predicate: (entity: Entity, index: number) => boolean + ): Entity | undefined { + return this.findEntity(entities, predicate); + } + + public testSomeEntity( + entities: readonly Entity[], + predicate: (entity: Entity, index: number) => boolean + ): boolean { + return this.someEntity(entities, predicate); + } + + public testEveryEntity( + entities: readonly Entity[], + predicate: (entity: Entity, index: number) => boolean + ): boolean { + return this.everyEntity(entities, predicate); + } + } + + let helperSystem: HelperSystem; + + beforeEach(() => { + helperSystem = new HelperSystem(); + scene.addSystem(helperSystem); + }); + + afterEach(() => { + scene.removeSystem(helperSystem); + }); + + it('requireComponent 应该返回存在的组件', () => { + const component = helperSystem.testRequireComponent(entity, TestComponent); + expect(component).toBeDefined(); + expect(component.value).toBe(10); + }); + + it('requireComponent 应该在组件不存在时抛出错误', () => { + class NonExistentComponent extends Component {} + + expect(() => { + helperSystem.testRequireComponent(entity, NonExistentComponent); + }).toThrow(); + }); + + it('forEach 应该遍历所有实体', () => { + const entities = [entity]; + const visited: Entity[] = []; + + helperSystem.testForEach(entities, (e) => { + visited.push(e); + }); + + expect(visited).toHaveLength(1); + expect(visited[0]).toBe(entity); + }); + + it('filterEntities 应该正确过滤实体', () => { + const entity2 = scene.createEntity('entity2'); + entity2.addComponent(new TestComponent(20)); + scene.addEntity(entity2); + + const entities = [entity, entity2]; + + const filtered = helperSystem.testFilterEntities(entities, (e) => { + const comp = e.getComponent(TestComponent); + return comp !== null && comp.value > 15; + }); + + expect(filtered).toHaveLength(1); + expect(filtered[0]).toBe(entity2); + }); + + it('findEntity 应该返回第一个匹配的实体', () => { + const entities = [entity]; + + const found = helperSystem.testFindEntity(entities, (e) => { + const comp = e.getComponent(TestComponent); + return comp !== null && comp.value === 10; + }); + + expect(found).toBe(entity); + }); + + it('findEntity 应该在未找到时返回 undefined', () => { + const entities = [entity]; + + const found = helperSystem.testFindEntity(entities, () => false); + + expect(found).toBeUndefined(); + }); + + it('someEntity 应该正确判断是否存在匹配实体', () => { + const entities = [entity]; + + const hasMatch = helperSystem.testSomeEntity(entities, (e) => { + const comp = e.getComponent(TestComponent); + return comp !== null && comp.value === 10; + }); + + expect(hasMatch).toBe(true); + + const noMatch = helperSystem.testSomeEntity(entities, () => false); + expect(noMatch).toBe(false); + }); + + it('everyEntity 应该正确判断是否所有实体都匹配', () => { + const entities = [entity]; + + const allMatch = helperSystem.testEveryEntity(entities, (e) => { + return e.getComponent(TestComponent) !== null; + }); + + expect(allMatch).toBe(true); + + const notAllMatch = helperSystem.testEveryEntity(entities, () => false); + expect(notAllMatch).toBe(false); + }); + }); + }); \ No newline at end of file diff --git a/packages/core/tests/ECS/Utils/Matcher.test.ts b/packages/core/tests/ECS/Utils/Matcher.test.ts index ddb05fd1..23afe881 100644 --- a/packages/core/tests/ECS/Utils/Matcher.test.ts +++ b/packages/core/tests/ECS/Utils/Matcher.test.ts @@ -485,6 +485,59 @@ describe('Matcher', () => { }); }); + describe('nothing() 匹配器', () => { + test('nothing() 应该创建不匹配任何实体的匹配器', () => { + const matcher = Matcher.nothing(); + const condition = matcher.getCondition(); + + expect(condition.matchNothing).toBe(true); + expect(condition.all).toHaveLength(0); + expect(condition.any).toHaveLength(0); + expect(condition.none).toHaveLength(0); + }); + + test('isNothing() 应该正确判断 nothing 匹配器', () => { + const nothingMatcher = Matcher.nothing(); + expect(nothingMatcher.isNothing()).toBe(true); + + const normalMatcher = Matcher.all(Position); + expect(normalMatcher.isNothing()).toBe(false); + + const emptyMatcher = Matcher.empty(); + expect(emptyMatcher.isNothing()).toBe(false); + }); + + test('isEmpty() 不应该将 nothing 匹配器视为空', () => { + const nothingMatcher = Matcher.nothing(); + // nothing 匹配器有明确的语义,不应该算作空 + expect(nothingMatcher.isEmpty()).toBe(false); + }); + + test('toString() 应该正确处理 nothing 匹配器', () => { + const matcher = Matcher.nothing(); + const str = matcher.toString(); + + expect(str).toBe('Matcher[nothing]'); + }); + + test('clone() 应该正确复制 nothing 匹配器', () => { + const original = Matcher.nothing(); + const cloned = original.clone(); + + expect(cloned.isNothing()).toBe(true); + expect(cloned.getCondition().matchNothing).toBe(true); + }); + + test('reset() 应该清除 matchNothing 标志', () => { + const matcher = Matcher.nothing(); + expect(matcher.isNothing()).toBe(true); + + matcher.reset(); + expect(matcher.isNothing()).toBe(false); + expect(matcher.isEmpty()).toBe(true); + }); + }); + describe('类型安全性', () => { test('ComponentType 应该正确工作', () => { // 这个测试主要是确保类型编译正确 diff --git a/packages/ecs-engine-bindgen/package.json b/packages/ecs-engine-bindgen/package.json index 61f41592..42ca2939 100644 --- a/packages/ecs-engine-bindgen/package.json +++ b/packages/ecs-engine-bindgen/package.json @@ -36,7 +36,7 @@ ], "author": "ESEngine Team", "license": "MIT", - "dependencies": { + "peerDependencies": { "@esengine/ecs-framework": "workspace:*", "@esengine/ecs-components": "workspace:*", "@esengine/asset-system": "workspace:*" diff --git a/packages/ecs-engine-bindgen/src/core/EngineBridge.ts b/packages/ecs-engine-bindgen/src/core/EngineBridge.ts index 69fcecbe..ab0f7fde 100644 --- a/packages/ecs-engine-bindgen/src/core/EngineBridge.ts +++ b/packages/ecs-engine-bindgen/src/core/EngineBridge.ts @@ -275,6 +275,18 @@ export class EngineBridge implements IEngineBridge { } } + /** + * Render sprites as overlay without clearing the screen. + * 渲染精灵作为叠加层,不清除屏幕。 + * + * This is used for UI rendering on top of world content. + * 用于在世界内容上渲染 UI。 + */ + renderOverlay(): void { + if (!this.initialized) return; + this.getEngine().renderOverlay(); + } + /** * Load a texture. * 加载纹理。 @@ -622,6 +634,109 @@ export class EngineBridge implements IEngineBridge { return this.getEngine().getViewportIds(); } + // ===== Screen Space Mode API ===== + // ===== 屏幕空间模式 API ===== + + // Saved world space camera state + // 保存的世界空间相机状态 + private savedWorldCamera: CameraConfig | null = null; + + /** + * Push screen space rendering mode. + * 进入屏幕空间渲染模式。 + * + * Saves the current world camera and switches to a fixed orthographic projection + * centered at (0, 0) with the specified canvas size. + * 保存当前世界相机并切换到以 (0, 0) 为中心的固定正交投影。 + * + * @param canvasWidth - UI canvas width (design resolution) | UI 画布宽度(设计分辨率) + * @param canvasHeight - UI canvas height (design resolution) | UI 画布高度(设计分辨率) + */ + pushScreenSpaceMode(canvasWidth: number, canvasHeight: number): void { + if (!this.initialized) return; + + // Save current world camera state + // 保存当前世界相机状态 + this.savedWorldCamera = this.getCamera(); + + // Switch to screen space camera: + // - Position at origin (0, 0) + // - Zoom = 1 (1 pixel = 1 world unit) + // - No rotation + // 切换到屏幕空间相机: + // - 位置在原点 (0, 0) + // - 缩放 = 1(1 像素 = 1 世界单位) + // - 无旋转 + // + // For screen space UI, we want the camera to show exactly canvasWidth x canvasHeight pixels + // centered at (0, 0). This means the visible area is: + // X: [-canvasWidth/2, canvasWidth/2] + // Y: [-canvasHeight/2, canvasHeight/2] + // 对于屏幕空间 UI,我们希望相机精确显示 canvasWidth x canvasHeight 像素 + // 以 (0, 0) 为中心。这意味着可见区域是: + // X: [-canvasWidth/2, canvasWidth/2] + // Y: [-canvasHeight/2, canvasHeight/2] + + // Get current viewport size to calculate proper zoom + // 获取当前视口尺寸以计算正确的缩放 + // Note: This assumes canvas.width/height match actual rendering size + // 注意:这假设 canvas.width/height 与实际渲染尺寸匹配 + const canvas = document.getElementById(this.config.canvasId) as HTMLCanvasElement; + if (canvas) { + // Calculate zoom so that canvasWidth x canvasHeight fits exactly in the viewport + // 计算缩放使 canvasWidth x canvasHeight 正好适合视口 + // zoom = viewport_size / world_visible_size + // For UI, we want 1 UI unit = 1 pixel on screen when canvas matches viewport + // 对于 UI,当画布与视口匹配时,我们希望 1 UI 单位 = 1 屏幕像素 + const viewportWidth = canvas.width; + const viewportHeight = canvas.height; + + // Calculate zoom based on the design canvas size vs actual viewport + // 根据设计画布尺寸与实际视口计算缩放 + // This scales UI to fit the viewport while maintaining aspect ratio + const zoomX = viewportWidth / canvasWidth; + const zoomY = viewportHeight / canvasHeight; + + // Use minimum to ensure entire canvas is visible (letterbox if needed) + // 使用最小值确保整个画布可见(如需要则显示黑边) + const zoom = Math.min(zoomX, zoomY); + + this.setCamera({ + x: 0, + y: 0, + zoom: zoom, + rotation: 0 + }); + } else { + // Fallback: use zoom = 1 + // 回退:使用 zoom = 1 + this.setCamera({ + x: 0, + y: 0, + zoom: 1, + rotation: 0 + }); + } + } + + /** + * Pop screen space rendering mode. + * 退出屏幕空间渲染模式。 + * + * Restores the previously saved world camera. + * 恢复之前保存的世界相机。 + */ + popScreenSpaceMode(): void { + if (!this.initialized) return; + + // Restore world camera + // 恢复世界相机 + if (this.savedWorldCamera) { + this.setCamera(this.savedWorldCamera); + this.savedWorldCamera = null; + } + } + /** * Dispose the bridge and release resources. * 销毁桥接并释放资源。 diff --git a/packages/ecs-engine-bindgen/src/index.ts b/packages/ecs-engine-bindgen/src/index.ts index f1e7b857..af231e0f 100644 --- a/packages/ecs-engine-bindgen/src/index.ts +++ b/packages/ecs-engine-bindgen/src/index.ts @@ -8,6 +8,6 @@ export { EngineBridge, EngineBridgeConfig } from './core/EngineBridge'; export { RenderBatcher } from './core/RenderBatcher'; export { SpriteRenderHelper, ITransformComponent } from './core/SpriteRenderHelper'; -export { EngineRenderSystem, type TransformComponentType, type IRenderDataProvider, type GizmoDataProviderFn, type HasGizmoProviderFn } from './systems/EngineRenderSystem'; +export { EngineRenderSystem, type TransformComponentType, type IRenderDataProvider, type IUIRenderDataProvider, type GizmoDataProviderFn, type HasGizmoProviderFn, type ProviderRenderData } from './systems/EngineRenderSystem'; export { CameraSystem } from './systems/CameraSystem'; export * from './types'; diff --git a/packages/ecs-engine-bindgen/src/systems/EngineRenderSystem.ts b/packages/ecs-engine-bindgen/src/systems/EngineRenderSystem.ts index c78033c6..ec8ab5a7 100644 --- a/packages/ecs-engine-bindgen/src/systems/EngineRenderSystem.ts +++ b/packages/ecs-engine-bindgen/src/systems/EngineRenderSystem.ts @@ -34,6 +34,22 @@ export interface IRenderDataProvider { getRenderData(): readonly ProviderRenderData[]; } +/** + * Interface for UI render data providers + * UI 渲染数据提供者接口 + * + * All UI is rendered in Screen Space with independent orthographic projection. + * 所有 UI 都在屏幕空间渲染,使用独立的正交投影。 + */ +export interface IUIRenderDataProvider extends IRenderDataProvider { + /** Get UI render data | 获取 UI 渲染数据 */ + getRenderData(): readonly ProviderRenderData[]; + /** @deprecated Use getRenderData() instead */ + getScreenSpaceRenderData?(): readonly ProviderRenderData[]; + /** @deprecated World space UI is no longer supported */ + getWorldSpaceRenderData?(): readonly ProviderRenderData[]; +} + /** * Internal gizmo color interface (duck-typed, compatible with editor-core GizmoColor) * 内部 gizmo 颜色接口(鸭子类型,与 editor-core GizmoColor 兼容) @@ -145,6 +161,22 @@ export class EngineRenderSystem extends EntitySystem { private gizmoDataProvider: GizmoDataProviderFn | null = null; private hasGizmoProvider: HasGizmoProviderFn | null = null; + // UI Canvas boundary settings + // UI 画布边界设置 + private uiCanvasWidth: number = 0; + private uiCanvasHeight: number = 0; + private showUICanvasBoundary: boolean = true; + + // UI render data provider (supports screen space and world space) + // UI 渲染数据提供者(支持屏幕空间和世界空间) + private uiRenderDataProvider: IUIRenderDataProvider | null = null; + + // Preview mode flag: when true, UI uses screen space overlay projection + // when false (editor mode), UI renders in world space following editor camera + // 预览模式标志:为 true 时,UI 使用屏幕空间叠加投影 + // 为 false(编辑器模式)时,UI 在世界空间渲染,跟随编辑器相机 + private previewMode: boolean = false; + /** * Create a new engine render system. * 创建新的引擎渲染系统。 @@ -190,6 +222,14 @@ export class EngineRenderSystem extends EntitySystem { * Process all matched entities. * 处理所有匹配的实体。 * + * Rendering is done in two passes: + * 1. World Pass: World sprites, tilemaps, gizmos (affected by world camera) + * 2. UI Pass: Screen space UI (independent orthographic projection, overlaid on world) + * + * 渲染分两个阶段进行: + * 1. 世界阶段:世界 Sprite、瓦片地图、Gizmo(受世界相机影响) + * 2. UI 阶段:屏幕空间 UI(独立正交投影,叠加在世界之上) + * * @param entities - Entities to process | 要处理的实体 */ protected override process(entities: readonly Entity[]): void { @@ -197,6 +237,11 @@ export class EngineRenderSystem extends EntitySystem { // 清空并重用映射用于绘制gizmo this.entityRenderMap.clear(); + // ===== Pass 1: World Space Rendering ===== + // ===== 阶段 1:世界空间渲染 ===== + // This includes world sprites, tilemaps, and world space UI + // 包括世界 Sprite、瓦片地图和世界空间 UI + // Collect all render items with sorting order // 收集所有渲染项及其排序顺序 const renderItems: Array<{ sortingOrder: number; sprites: SpriteRenderData[] }> = []; @@ -259,7 +304,6 @@ export class EngineRenderSystem extends EntitySystem { } // Collect render data from providers (e.g., tilemap) - // 收集来自提供者的渲染数据(如瓦片地图) for (const provider of this.renderDataProviders) { const renderDataList = provider.getRenderData(); for (const data of renderDataList) { @@ -297,6 +341,18 @@ export class EngineRenderSystem extends EntitySystem { } } + // Collect UI render data if in editor mode (renders in world space) + // 如果在编辑器模式,收集 UI 渲染数据(在世界空间渲染) + if (!this.previewMode && this.uiRenderDataProvider) { + const uiRenderData = this.uiRenderDataProvider.getRenderData(); + for (const data of uiRenderData) { + const uiSprites = this.convertProviderDataToSprites(data); + if (uiSprites.length > 0) { + renderItems.push({ sortingOrder: data.sortingOrder, sprites: uiSprites }); + } + } + } + // Sort by sortingOrder (lower values render first, appear behind) // 按 sortingOrder 排序(值越小越先渲染,显示在后面) renderItems.sort((a, b) => a.sortingOrder - b.sortingOrder); @@ -332,7 +388,127 @@ export class EngineRenderSystem extends EntitySystem { this.drawCameraFrustums(); } + // Draw UI canvas boundary + // 绘制 UI 画布边界 + if (this.showGizmos && this.showUICanvasBoundary && this.uiCanvasWidth > 0 && this.uiCanvasHeight > 0) { + this.drawUICanvasBoundary(); + } + + // ===== World Pass: Render world content ===== + // ===== 世界阶段:渲染世界内容 ===== this.bridge.render(); + + // ===== Pass 2: Screen Space UI Rendering (Preview Mode Only) ===== + // ===== 阶段 2:屏幕空间 UI 渲染(仅预览模式)===== + // UI is rendered on top of world content with independent projection + // UI 使用独立投影渲染在世界内容之上 + // Only in preview mode - in editor mode, UI is rendered in world space above + // 仅在预览模式 - 在编辑器模式,UI 在上面的世界空间渲染 + if (this.previewMode) { + this.renderScreenSpaceUI(); + } + } + + /** + * Render screen space UI with fixed orthographic projection. + * 使用固定正交投影渲染屏幕空间 UI。 + * + * Screen space UI is rendered with an independent orthographic projection + * based on the UI canvas size, not affected by the world camera. + * 屏幕空间 UI 使用基于 UI 画布尺寸的独立正交投影渲染,不受世界相机影响。 + */ + private renderScreenSpaceUI(): void { + if (!this.uiRenderDataProvider) { + return; + } + + // Get all UI render data (now only screen space) + // 获取所有 UI 渲染数据(现在只有屏幕空间) + const uiRenderData = this.uiRenderDataProvider.getRenderData(); + if (uiRenderData.length === 0) { + return; + } + + // Switch to screen space projection + // 切换到屏幕空间投影 + // Use UI canvas size for the orthographic projection + // 使用 UI 画布尺寸进行正交投影 + const canvasWidth = this.uiCanvasWidth > 0 ? this.uiCanvasWidth : 1920; + const canvasHeight = this.uiCanvasHeight > 0 ? this.uiCanvasHeight : 1080; + + // Save current camera state and switch to screen space mode + // 保存当前相机状态并切换到屏幕空间模式 + this.bridge.pushScreenSpaceMode(canvasWidth, canvasHeight); + + // Clear batcher for screen space content + this.batcher.clear(); + + // Collect screen space UI render items + const screenSpaceItems: Array<{ sortingOrder: number; sprites: SpriteRenderData[] }> = []; + + for (const data of uiRenderData) { + const uiSprites = this.convertProviderDataToSprites(data); + if (uiSprites.length > 0) { + screenSpaceItems.push({ sortingOrder: data.sortingOrder, sprites: uiSprites }); + } + } + + // Sort by sortingOrder + screenSpaceItems.sort((a, b) => a.sortingOrder - b.sortingOrder); + + // Submit screen space UI sprites + for (const item of screenSpaceItems) { + for (const sprite of item.sprites) { + this.batcher.addSprite(sprite); + } + } + + if (!this.batcher.isEmpty) { + const sprites = this.batcher.getSprites(); + this.bridge.submitSprites(sprites); + // Render overlay (without clearing screen) + // 渲染叠加层(不清屏) + this.bridge.renderOverlay(); + } + + // Restore world space camera + // 恢复世界空间相机 + this.bridge.popScreenSpaceMode(); + } + + /** + * Convert provider render data to sprite render data array. + * 将提供者渲染数据转换为 Sprite 渲染数据数组。 + */ + private convertProviderDataToSprites(data: ProviderRenderData): SpriteRenderData[] { + // Get texture ID - load from path if needed + let textureId = data.textureIds[0] || 0; + if (textureId === 0 && data.texturePath) { + textureId = this.bridge.getOrLoadTextureByPath(data.texturePath); + } + + const sprites: SpriteRenderData[] = []; + for (let i = 0; i < data.tileCount; i++) { + const tOffset = i * 7; + const uvOffset = i * 4; + + const renderData: SpriteRenderData = { + x: data.transforms[tOffset], + y: data.transforms[tOffset + 1], + rotation: data.transforms[tOffset + 2], + scaleX: data.transforms[tOffset + 3], + scaleY: data.transforms[tOffset + 4], + originX: data.transforms[tOffset + 5], + originY: data.transforms[tOffset + 6], + textureId, + uv: [data.uvs[uvOffset], data.uvs[uvOffset + 1], data.uvs[uvOffset + 2], data.uvs[uvOffset + 3]], + color: data.colors[i] + }; + + sprites.push(renderData); + } + + return sprites; } /** @@ -675,6 +851,78 @@ export class EngineRenderSystem extends EntitySystem { } } + /** + * Draw UI canvas boundary. + * 绘制 UI 画布边界。 + * + * Shows the design resolution boundary of the UI canvas. + * 显示 UI 画布的设计分辨率边界。 + */ + private drawUICanvasBoundary(): void { + const w = this.uiCanvasWidth; + const h = this.uiCanvasHeight; + + // Canvas is centered at (0, 0) in Y-up coordinate system + // 画布以 (0, 0) 为中心,Y 轴向上坐标系 + // Bottom-left: (-w/2, -h/2), Top-right: (w/2, h/2) + + // Draw the boundary as a rectangle + // 绘制边界矩形 + // Using origin (0, 0) means position is bottom-left corner + // 使用 origin (0, 0) 表示位置是左下角 + this.bridge.addGizmoRect( + -w / 2, // x: left edge + -h / 2, // y: bottom edge (in Y-up system) + w, // width + h, // height + 0, // rotation + 0, // originX: left + 0, // originY: bottom + 0.5, 0.8, 1.0, 0.6, // Light blue color for UI canvas boundary + false // Don't show transform handles + ); + + // Draw corner markers for better visibility + // 绘制角标记以提高可见性 + const markerSize = 20; + const markerColor = { r: 0.5, g: 0.8, b: 1.0, a: 1.0 }; + + // Top-left corner marker (L shape) + const corners = [ + // Top-left + { x: -w / 2, y: h / 2 - markerSize, ex: -w / 2, ey: h / 2 }, + { x: -w / 2, y: h / 2, ex: -w / 2 + markerSize, ey: h / 2 }, + // Top-right + { x: w / 2 - markerSize, y: h / 2, ex: w / 2, ey: h / 2 }, + { x: w / 2, y: h / 2, ex: w / 2, ey: h / 2 - markerSize }, + // Bottom-right + { x: w / 2, y: -h / 2 + markerSize, ex: w / 2, ey: -h / 2 }, + { x: w / 2, y: -h / 2, ex: w / 2 - markerSize, ey: -h / 2 }, + // Bottom-left + { x: -w / 2 + markerSize, y: -h / 2, ex: -w / 2, ey: -h / 2 }, + { x: -w / 2, y: -h / 2, ex: -w / 2, ey: -h / 2 + markerSize }, + ]; + + for (const line of corners) { + const dx = line.ex - line.x; + const dy = line.ey - line.y; + const length = Math.sqrt(dx * dx + dy * dy); + const angle = Math.atan2(dy, dx); + + this.bridge.addGizmoRect( + (line.x + line.ex) / 2, + (line.y + line.ey) / 2, + length, + 2, // Line thickness + angle, + 0.5, + 0.5, + markerColor.r, markerColor.g, markerColor.b, markerColor.a, + false + ); + } + } + /** * Set gizmo registry functions. * 设置 gizmo 注册表函数。 @@ -711,6 +959,42 @@ export class EngineRenderSystem extends EntitySystem { return this.showGizmos; } + /** + * Set UI canvas size for boundary display. + * 设置 UI 画布尺寸以显示边界。 + * + * @param width - Canvas width (design resolution) | 画布宽度(设计分辨率) + * @param height - Canvas height (design resolution) | 画布高度(设计分辨率) + */ + setUICanvasSize(width: number, height: number): void { + this.uiCanvasWidth = width; + this.uiCanvasHeight = height; + } + + /** + * Get UI canvas size. + * 获取 UI 画布尺寸。 + */ + getUICanvasSize(): { width: number; height: number } { + return { width: this.uiCanvasWidth, height: this.uiCanvasHeight }; + } + + /** + * Set UI canvas boundary visibility. + * 设置 UI 画布边界可见性。 + */ + setShowUICanvasBoundary(show: boolean): void { + this.showUICanvasBoundary = show; + } + + /** + * Get UI canvas boundary visibility. + * 获取 UI 画布边界可见性。 + */ + getShowUICanvasBoundary(): boolean { + return this.showUICanvasBoundary; + } + /** * Set selected entity IDs. * 设置选中的实体ID。 @@ -798,6 +1082,51 @@ export class EngineRenderSystem extends EntitySystem { } } + /** + * Set the UI render data provider. + * 设置 UI 渲染数据提供者。 + * + * The UI render data provider supports both screen space and world space UI. + * UI 渲染数据提供者支持屏幕空间和世界空间 UI。 + * + * @param provider - UI render data provider | UI 渲染数据提供者 + */ + setUIRenderDataProvider(provider: IUIRenderDataProvider | null): void { + this.uiRenderDataProvider = provider; + } + + /** + * Get the UI render data provider. + * 获取 UI 渲染数据提供者。 + */ + getUIRenderDataProvider(): IUIRenderDataProvider | null { + return this.uiRenderDataProvider; + } + + /** + * Set preview mode. + * 设置预览模式。 + * + * In preview mode (true): UI uses screen space overlay projection, independent of world camera. + * In editor mode (false): UI renders in world space, following the editor camera. + * + * 预览模式(true):UI 使用屏幕空间叠加投影,独立于世界相机。 + * 编辑器模式(false):UI 在世界空间渲染,跟随编辑器相机。 + * + * @param mode - True for preview mode, false for editor mode + */ + setPreviewMode(mode: boolean): void { + this.previewMode = mode; + } + + /** + * Get preview mode. + * 获取预览模式。 + */ + isPreviewMode(): boolean { + return this.previewMode; + } + /** * Get the number of sprites rendered. * 获取渲染的精灵数量。 diff --git a/packages/ecs-engine-bindgen/src/wasm/es_engine.d.ts b/packages/ecs-engine-bindgen/src/wasm/es_engine.d.ts index 584cf4e6..681ac314 100644 --- a/packages/ecs-engine-bindgen/src/wasm/es_engine.d.ts +++ b/packages/ecs-engine-bindgen/src/wasm/es_engine.d.ts @@ -85,6 +85,14 @@ export class GameEngine { * * `show_handles` - Whether to show transform handles | 是否显示变换手柄 */ addGizmoRect(x: number, y: number, width: number, height: number, rotation: number, origin_x: number, origin_y: number, r: number, g: number, b: number, a: number, show_handles: boolean): void; + /** + * Render sprites as overlay (without clearing screen). + * 渲染精灵作为叠加层(不清除屏幕)。 + * + * This is used for UI rendering on top of the world content. + * 用于在世界内容上渲染 UI。 + */ + renderOverlay(): void; /** * Resize a specific viewport. * 调整特定视口大小。 @@ -259,6 +267,7 @@ export interface InitOutput { readonly gameengine_new: (a: number, b: number) => [number, number, number]; readonly gameengine_registerViewport: (a: number, b: number, c: number, d: number, e: number) => [number, number]; readonly gameengine_render: (a: number) => [number, number]; + readonly gameengine_renderOverlay: (a: number) => [number, number]; readonly gameengine_renderToViewport: (a: number, b: number, c: number) => [number, number]; readonly gameengine_resize: (a: number, b: number, c: number) => void; readonly gameengine_resizeViewport: (a: number, b: number, c: number, d: number, e: number) => void; diff --git a/packages/editor-app/index.html b/packages/editor-app/index.html index 04028850..4b755ec8 100644 --- a/packages/editor-app/index.html +++ b/packages/editor-app/index.html @@ -3,7 +3,7 @@ - ECS Framework Editor + ESEngine Editor diff --git a/packages/editor-app/package.json b/packages/editor-app/package.json index 912c37b9..7918cb19 100644 --- a/packages/editor-app/package.json +++ b/packages/editor-app/package.json @@ -1,7 +1,7 @@ { "name": "@esengine/editor-app", "version": "1.0.9", - "description": "ECS Framework Editor Application - Cross-platform desktop editor", + "description": "ESEngine Editor Application - Cross-platform desktop editor", "type": "module", "private": true, "scripts": { @@ -17,11 +17,10 @@ "dependencies": { "@esengine/asset-system": "workspace:*", "@esengine/behavior-tree": "workspace:*", + "@esengine/editor-runtime": "workspace:*", "@esengine/ecs-components": "workspace:*", "@esengine/tilemap": "workspace:*", - "@esengine/tilemap-editor": "workspace:*", "@esengine/ui": "workspace:*", - "@esengine/ui-editor": "workspace:*", "@esengine/ecs-engine-bindgen": "workspace:*", "@esengine/ecs-framework": "workspace:*", "@esengine/editor-core": "workspace:*", diff --git a/packages/editor-app/public/runtime.config.json b/packages/editor-app/public/runtime.config.json index 11d59888..903d87ae 100644 --- a/packages/editor-app/public/runtime.config.json +++ b/packages/editor-app/public/runtime.config.json @@ -24,4 +24,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/editor-app/runtime.config.json b/packages/editor-app/runtime.config.json index 11d59888..903d87ae 100644 --- a/packages/editor-app/runtime.config.json +++ b/packages/editor-app/runtime.config.json @@ -24,4 +24,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/editor-app/src-tauri/Cargo.toml b/packages/editor-app/src-tauri/Cargo.toml index 3025ff86..fc3c7b58 100644 --- a/packages/editor-app/src-tauri/Cargo.toml +++ b/packages/editor-app/src-tauri/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ecs-editor" version = "1.0.0" -description = "ECS Framework Editor - Cross-platform desktop editor" +description = "ESEngine Editor - Cross-platform desktop editor" authors = ["yhh"] edition = "2021" diff --git a/packages/editor-app/src-tauri/src/main.rs b/packages/editor-app/src-tauri/src/main.rs index 07520f2b..df7c2fee 100644 --- a/packages/editor-app/src-tauri/src/main.rs +++ b/packages/editor-app/src-tauri/src/main.rs @@ -1,4 +1,4 @@ -//! ECS Framework Editor - Tauri Backend +//! ESEngine Editor - Tauri Backend //! //! Clean entry point that handles application setup and command registration. diff --git a/packages/editor-app/src-tauri/tauri.conf.json b/packages/editor-app/src-tauri/tauri.conf.json index a9b4f128..22743bb6 100644 --- a/packages/editor-app/src-tauri/tauri.conf.json +++ b/packages/editor-app/src-tauri/tauri.conf.json @@ -1,5 +1,5 @@ { - "productName": "ECS Framework Editor", + "productName": "ESEngine Editor", "version": "1.0.9", "identifier": "com.esengine.editor", "build": { @@ -35,7 +35,7 @@ "ecs" ], "name": "ECS Scene File", - "description": "ECS Framework Scene File", + "description": "ESEngine Scene File", "role": "Editor" } ], @@ -51,7 +51,7 @@ "app": { "windows": [ { - "title": "ECS Framework Editor", + "title": "ESEngine Editor", "width": 1280, "height": 800, "minWidth": 800, diff --git a/packages/editor-app/src/App.tsx b/packages/editor-app/src/App.tsx index af6c65a2..2e882a12 100644 --- a/packages/editor-app/src/App.tsx +++ b/packages/editor-app/src/App.tsx @@ -10,7 +10,7 @@ import * as ECSFramework from '@esengine/ecs-framework'; (window as any).ReactDOM = ReactDOM; (window as any).ReactJSXRuntime = ReactJSXRuntime; import { - EditorPluginManager, + PluginManager, UIRegistry, MessageHub, EntityStoreService, @@ -36,7 +36,6 @@ import { Inspector } from './components/inspectors/Inspector'; import { AssetBrowser } from './components/AssetBrowser'; import { ConsolePanel } from './components/ConsolePanel'; import { Viewport } from './components/Viewport'; -import { PluginManagerWindow } from './components/PluginManagerWindow'; import { ProfilerWindow } from './components/ProfilerWindow'; import { PortManager } from './components/PortManager'; import { SettingsWindow } from './components/SettingsWindow'; @@ -46,15 +45,11 @@ import { ConfirmDialog } from './components/ConfirmDialog'; import { PluginGeneratorWindow } from './components/PluginGeneratorWindow'; import { ToastProvider, useToast } from './components/Toast'; import { MenuBar } from './components/MenuBar'; -import { UserProfile } from './components/UserProfile'; -import { UserDashboard } from './components/UserDashboard'; import { FlexLayoutDockContainer, FlexDockPanel } from './components/FlexLayoutDockContainer'; import { TauriAPI } from './api/tauri'; import { SettingsService } from './services/SettingsService'; import { PluginLoader } from './services/PluginLoader'; -import { GitHubService } from './services/GitHubService'; -import { PluginPublishWizard } from './components/PluginPublishWizard'; -import { GitHubLoginDialog } from './components/GitHubLoginDialog'; +import { EngineService } from './services/EngineService'; import { CompilerConfigDialog } from './components/CompilerConfigDialog'; import { checkForUpdatesOnStartup } from './utils/updater'; import { useLocale } from './hooks/useLocale'; @@ -83,14 +78,13 @@ const logger = createLogger('App'); function App() { const initRef = useRef(false); const [pluginLoader] = useState(() => new PluginLoader()); - const [githubService] = useState(() => new GitHubService()); const { showToast, hideToast } = useToast(); const [initialized, setInitialized] = useState(false); const [projectLoaded, setProjectLoaded] = useState(false); const [isLoading, setIsLoading] = useState(false); const [loadingMessage, setLoadingMessage] = useState(''); const [currentProjectPath, setCurrentProjectPath] = useState(null); - const [pluginManager, setPluginManager] = useState(null); + const [pluginManager, setPluginManager] = useState(null); const [entityStore, setEntityStore] = useState(null); const [messageHub, setMessageHub] = useState(null); const [inspectorRegistry, setInspectorRegistry] = useState(null); @@ -117,7 +111,6 @@ function App() { const [showProjectWizard, setShowProjectWizard] = useState(false); const { - showPluginManager, setShowPluginManager, showProfiler, setShowProfiler, showPortManager, setShowPortManager, showSettings, setShowSettings, @@ -126,12 +119,11 @@ function App() { errorDialog, setErrorDialog, confirmDialog, setConfirmDialog } = useDialogStore(); + const [settingsInitialCategory, setSettingsInitialCategory] = useState(undefined); const [activeDynamicPanels, setActiveDynamicPanels] = useState([]); const [activePanelId, setActivePanelId] = useState(undefined); const [dynamicPanelTitles, setDynamicPanelTitles] = useState>(new Map()); const [isEditorFullscreen, setIsEditorFullscreen] = useState(false); - const [showLoginDialog, setShowLoginDialog] = useState(false); - const [showDashboard, setShowDashboard] = useState(false); const [compilerDialog, setCompilerDialog] = useState<{ isOpen: boolean; compilerId: string; @@ -274,6 +266,9 @@ function App() { const pluginInstaller = new PluginInstaller(); await pluginInstaller.installBuiltinPlugins(services.pluginManager); + // 初始化编辑器模块(安装设置、面板等) + await services.pluginManager.initializeEditor(Core.services); + services.notification.setCallbacks(showToast, hideToast); (services.dialog as IDialogExtended).setConfirmCallback(setConfirmDialog); @@ -283,7 +278,9 @@ function App() { if (windowId === 'profiler') { setShowProfiler(true); } else if (windowId === 'pluginManager') { - setShowPluginManager(true); + // 插件管理现在整合到设置窗口中 + setSettingsInitialCategory('plugins'); + setShowSettings(true); } }); @@ -370,7 +367,7 @@ function App() { const handleOpenRecentProject = async (projectPath: string) => { try { setIsLoading(true); - setLoadingMessage(locale === 'zh' ? '步骤 1/2: 打开项目配置...' : 'Step 1/2: Opening project config...'); + setLoadingMessage(locale === 'zh' ? '步骤 1/3: 打开项目配置...' : 'Step 1/3: Opening project config...'); const projectService = Core.services.resolve(ProjectService); @@ -385,21 +382,40 @@ function App() { // 设置 Tauri project:// 协议的基础路径(用于加载插件等项目文件) await TauriAPI.setProjectBasePath(projectPath); + const settings = SettingsService.getInstance(); + settings.addRecentProject(projectPath); + + setCurrentProjectPath(projectPath); + // 设置 projectLoaded 为 true,触发主界面渲染(包括 Viewport) + setProjectLoaded(true); + + // 等待引擎初始化完成(Viewport 渲染后会触发引擎初始化) + setLoadingMessage(locale === 'zh' ? '步骤 2/3: 初始化引擎和模块...' : 'Step 2/3: Initializing engine and modules...'); + const engineService = EngineService.getInstance(); + + // 等待引擎初始化(最多等待 30 秒,因为需要等待 Viewport 渲染) + const engineReady = await engineService.waitForInitialization(30000); + if (!engineReady) { + throw new Error(locale === 'zh' ? '引擎初始化超时' : 'Engine initialization timeout'); + } + + // 初始化模块系统(所有插件的 runtimeModule 会在 PluginManager 安装时自动注册) + await engineService.initializeModuleSystems(); + + // 应用项目的 UI 设计分辨率 + // Apply project's UI design resolution + const uiResolution = projectService.getUIDesignResolution(); + engineService.setUICanvasSize(uiResolution.width, uiResolution.height); + setStatus(t('header.status.projectOpened')); - setLoadingMessage(locale === 'zh' ? '步骤 2/2: 初始化场景...' : 'Step 2/2: Initializing scene...'); + setLoadingMessage(locale === 'zh' ? '步骤 3/3: 初始化场景...' : 'Step 3/3: Initializing scene...'); const sceneManagerService = Core.services.resolve(SceneManagerService); if (sceneManagerService) { await sceneManagerService.newScene(); } - const settings = SettingsService.getInstance(); - settings.addRecentProject(projectPath); - - setCurrentProjectPath(projectPath); - setProjectLoaded(true); - if (pluginManager) { setLoadingMessage(locale === 'zh' ? '加载项目插件...' : 'Loading project plugins...'); await pluginLoader.loadProjectPlugins(projectPath, pluginManager); @@ -605,9 +621,21 @@ function App() { }; const handleCloseProject = async () => { + // 卸载项目插件 if (pluginManager) { await pluginLoader.unloadProjectPlugins(pluginManager); } + + // 清理模块系统 + const engineService = EngineService.getInstance(); + engineService.clearModuleSystems(); + + // 关闭 ProjectService 中的项目 + const projectService = Core.services.tryResolve(ProjectService); + if (projectService) { + await projectService.closeProject(); + } + setProjectLoaded(false); setCurrentProjectPath(null); setIsProfilerMode(false); @@ -623,12 +651,7 @@ function App() { // 通知所有已加载的插件更新语言 if (pluginManager) { - const allPlugins = pluginManager.getAllEditorPlugins(); - allPlugins.forEach((plugin) => { - if (plugin.setLocale) { - plugin.setLocale(newLocale); - } - }); + pluginManager.setLocale(newLocale); // 通过 MessageHub 通知需要重新获取节点模板 if (messageHub) { @@ -747,10 +770,7 @@ function App() { ]; } - const enabledPlugins = pluginManager.getAllPluginMetadata() - .filter((p) => p.enabled) - .map((p) => p.name); - + // 获取启用的插件面板 const pluginPanels: FlexDockPanel[] = uiRegistry.getAllPanels() .filter((panelDesc) => { if (!panelDesc.component) { @@ -759,14 +779,7 @@ function App() { if (panelDesc.isDynamic) { return false; } - return enabledPlugins.some((pluginName) => { - const plugin = pluginManager.getEditorPlugin(pluginName); - if (plugin && plugin.registerPanels) { - const pluginPanels = plugin.registerPanels(); - return pluginPanels.some((p) => p.id === panelDesc.id); - } - return false; - }); + return true; }) .map((panelDesc) => { const Component = panelDesc.component; @@ -879,7 +892,7 @@ function App() { <>
{projectName} - ECS Framework Editor + ESEngine Editor
setShowPluginManager(true)} + onOpenPluginManager={() => { + setSettingsInitialCategory('plugins'); + setShowSettings(true); + }} onOpenProfiler={() => setShowProfiler(true)} onOpenPortManager={() => setShowPortManager(true)} onOpenSettings={() => setShowSettings(true)} @@ -904,12 +920,6 @@ function App() { onReloadPlugins={handleReloadPlugins} />
- setShowLoginDialog(true)} - onOpenDashboard={() => setShowDashboard(true)} - locale={locale} - />
-
- {plugin.description &&
{plugin.description}
} -
- - {(() => { - const CategoryIcon = (LucideIcons as any)[categoryIcons[plugin.category]]; - return CategoryIcon ? : null; - })()} - {getCategoryName(plugin.category)} - - {plugin.installedAt && ( - - {t('installed')}: {new Date(plugin.installedAt).toLocaleDateString()} - - )} -
-
- ); - }; - - const renderPluginList = (plugin: IEditorPluginMetadata) => { - const IconComponent = plugin.icon ? (LucideIcons as any)[plugin.icon] : null; - return ( -
-
- {IconComponent ? : } -
-
-
- {plugin.displayName} - v{plugin.version} -
- {plugin.description &&
{plugin.description}
} -
-
- {plugin.enabled ? ( - {t('enabled')} - ) : ( - {t('disabled')} - )} -
- -
- ); - }; - - return ( -
-
e.stopPropagation()}> -
-
- -

{t('title')}

-
- -
- -
- - -
- - {activeTab === 'installed' && ( - <> -
-
-
- - setFilter(e.target.value)} - /> -
-
-
-
- - - {enabledCount} {t('enabled')} - - - - {disabledCount} {t('disabled')} - -
- {onRefresh && ( - - )} -
- - -
-
-
- -
- {plugins.length === 0 ? ( -
- -

{t('noPlugins')}

-
- ) : ( -
- {Object.entries(pluginsByCategory).map(([category, categoryPlugins]) => { - const cat = category as EditorPluginCategory; - const isExpanded = expandedCategories.has(cat); - - return ( -
-
toggleCategory(cat)} - > - - - {(() => { - const CategoryIcon = (LucideIcons as any)[ - categoryIcons[cat] - ]; - return CategoryIcon ? : null; - })()} - - {getCategoryName(cat)} - - {categoryPlugins.length} - -
- - {isExpanded && ( -
- {viewMode === 'grid' - ? categoryPlugins.map(renderPluginCard) - : categoryPlugins.map(renderPluginList)} -
- )} -
- ); - })} -
- )} -
- - )} - - {activeTab === 'marketplace' && ( - - )} -
-
- ); -} diff --git a/packages/editor-app/src/components/PluginMarketPanel.tsx b/packages/editor-app/src/components/PluginMarketPanel.tsx deleted file mode 100644 index e480f7cb..00000000 --- a/packages/editor-app/src/components/PluginMarketPanel.tsx +++ /dev/null @@ -1,440 +0,0 @@ -import { useState, useEffect } from 'react'; -import * as LucideIcons from 'lucide-react'; -import { - Package, - Search, - Download, - CheckCircle, - ExternalLink, - Github, - Star, - AlertCircle, - RefreshCw, - Filter -} from 'lucide-react'; -import { open } from '@tauri-apps/plugin-shell'; -import type { PluginMarketService, PluginMarketMetadata } from '../services/PluginMarketService'; -import '../styles/PluginMarketPanel.css'; - -interface PluginMarketPanelProps { - marketService: PluginMarketService; - locale: string; - projectPath: string | null; - onReloadPlugins?: () => Promise; -} - -export function PluginMarketPanel({ marketService, locale, projectPath, onReloadPlugins }: PluginMarketPanelProps) { - const t = (key: string) => { - const translations: Record> = { - zh: { - title: '插件市场', - searchPlaceholder: '搜索插件...', - loading: '加载中...', - loadError: '无法连接到插件市场', - loadErrorDesc: '可能是网络连接问题,请检查您的网络设置后重试', - retry: '重试', - noPlugins: '没有找到插件', - install: '安装', - installed: '已安装', - update: '更新', - uninstall: '卸载', - viewSource: '查看源码', - official: '官方', - verified: '认证', - community: '社区', - filterAll: '全部', - filterOfficial: '官方插件', - filterCommunity: '社区插件', - categoryAll: '全部分类', - installing: '安装中...', - uninstalling: '卸载中...', - useDirectSource: '使用直连源', - useDirectSourceTip: '启用后直接从GitHub获取数据,绕过CDN缓存(适合测试)', - latest: '最新', - releaseNotes: '更新日志', - selectVersion: '选择版本', - noProjectOpen: '请先打开一个项目' - }, - en: { - title: 'Plugin Marketplace', - searchPlaceholder: 'Search plugins...', - loading: 'Loading...', - loadError: 'Unable to connect to plugin marketplace', - loadErrorDesc: 'This might be a network connection issue. Please check your network settings and try again', - retry: 'Retry', - noPlugins: 'No plugins found', - install: 'Install', - installed: 'Installed', - update: 'Update', - uninstall: 'Uninstall', - viewSource: 'View Source', - official: 'Official', - verified: 'Verified', - community: 'Community', - filterAll: 'All', - filterOfficial: 'Official', - filterCommunity: 'Community', - categoryAll: 'All Categories', - installing: 'Installing...', - uninstalling: 'Uninstalling...', - useDirectSource: 'Direct Source', - useDirectSourceTip: 'Fetch data directly from GitHub, bypassing CDN cache (for testing)', - latest: 'Latest', - releaseNotes: 'Release Notes', - selectVersion: 'Select Version', - noProjectOpen: 'Please open a project first' - } - }; - return translations[locale]?.[key] || translations.en?.[key] || key; - }; - - const [plugins, setPlugins] = useState([]); - const [filteredPlugins, setFilteredPlugins] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [searchQuery, setSearchQuery] = useState(''); - const [typeFilter, setTypeFilter] = useState<'all' | 'official' | 'community'>('all'); - const [categoryFilter, setCategoryFilter] = useState('all'); - const [installingPlugins, setInstallingPlugins] = useState>(new Set()); - const [useDirectSource, setUseDirectSource] = useState(marketService.isUsingDirectSource()); - - useEffect(() => { - loadPlugins(); - }, []); - - useEffect(() => { - filterPlugins(); - }, [plugins, searchQuery, typeFilter, categoryFilter]); - - const loadPlugins = async (bypassCache: boolean = false) => { - setLoading(true); - setError(null); - - try { - const pluginList = await marketService.fetchPluginList(bypassCache); - setPlugins(pluginList); - } catch (err) { - setError(err instanceof Error ? err.message : 'Unknown error'); - } finally { - setLoading(false); - } - }; - - const filterPlugins = () => { - let filtered = plugins; - - if (searchQuery) { - const query = searchQuery.toLowerCase(); - filtered = filtered.filter( - (p) => - p.name.toLowerCase().includes(query) || - p.description.toLowerCase().includes(query) || - p.tags?.some((tag) => tag.toLowerCase().includes(query)) - ); - } - - if (typeFilter !== 'all') { - filtered = filtered.filter((p) => p.category_type === typeFilter); - } - - if (categoryFilter !== 'all') { - filtered = filtered.filter((p) => p.category === categoryFilter); - } - - setFilteredPlugins(filtered); - }; - - const handleToggleDirectSource = () => { - const newValue = !useDirectSource; - setUseDirectSource(newValue); - marketService.setUseDirectSource(newValue); - loadPlugins(true); - }; - - const handleInstall = async (plugin: PluginMarketMetadata, version?: string) => { - if (!projectPath) { - alert(t('noProjectOpen') || 'Please open a project first'); - return; - } - - setInstallingPlugins((prev) => new Set(prev).add(plugin.id)); - - try { - await marketService.installPlugin(plugin, version, onReloadPlugins); - setPlugins([...plugins]); - } catch (error) { - console.error('Failed to install plugin:', error); - alert(`Failed to install ${plugin.name}: ${error}`); - } finally { - setInstallingPlugins((prev) => { - const next = new Set(prev); - next.delete(plugin.id); - return next; - }); - } - }; - - const handleUninstall = async (plugin: PluginMarketMetadata) => { - if (!confirm(`Are you sure you want to uninstall ${plugin.name}?`)) { - return; - } - - setInstallingPlugins((prev) => new Set(prev).add(plugin.id)); - - try { - await marketService.uninstallPlugin(plugin.id, onReloadPlugins); - setPlugins([...plugins]); - } catch (error) { - console.error('Failed to uninstall plugin:', error); - alert(`Failed to uninstall ${plugin.name}: ${error}`); - } finally { - setInstallingPlugins((prev) => { - const next = new Set(prev); - next.delete(plugin.id); - return next; - }); - } - }; - - const categories = ['all', ...Array.from(new Set(plugins.map((p) => p.category)))]; - - if (loading) { - return ( -
- -

{t('loading')}

-
- ); - } - - if (error) { - return ( -
- -

{t('loadError')}

-

{t('loadErrorDesc')}

-
-

{error}

-
- -
- ); - } - - return ( -
-
-
- - setSearchQuery(e.target.value)} - /> -
- -
- - - - - - - -
-
- -
- {filteredPlugins.length === 0 ? ( -
- -

{t('noPlugins')}

-
- ) : ( -
- {filteredPlugins.map((plugin) => ( - handleInstall(plugin, version)} - onUninstall={() => handleUninstall(plugin)} - t={t} - /> - ))} -
- )} -
-
- ); -} - -interface PluginMarketCardProps { - plugin: PluginMarketMetadata; - isInstalled: boolean; - hasUpdate: boolean; - isInstalling: boolean; - onInstall: (version?: string) => void; - onUninstall: () => void; - t: (key: string) => string; -} - -function PluginMarketCard({ - plugin, - isInstalled, - hasUpdate, - isInstalling, - onInstall, - onUninstall, - t -}: PluginMarketCardProps) { - const [selectedVersion, setSelectedVersion] = useState(plugin.latestVersion); - const [showVersions, setShowVersions] = useState(false); - - const IconComponent = plugin.icon ? (LucideIcons as any)[plugin.icon] : Package; - - const selectedVersionData = plugin.versions.find((v) => v.version === selectedVersion); - const multipleVersions = plugin.versions.length > 1; - - return ( -
-
-
- -
-
-
- {plugin.name} - {plugin.verified && ( - - - - )} -
-
- - - {plugin.author.name} - - {multipleVersions ? ( - - ) : ( - v{plugin.latestVersion} - )} -
-
-
- -
{plugin.description}
- - {selectedVersionData && selectedVersionData.changes && ( -
- {t('releaseNotes')} -

{selectedVersionData.changes}

-
- )} - - {plugin.tags && plugin.tags.length > 0 && ( -
- {plugin.tags.slice(0, 3).map((tag) => ( - - {tag} - - ))} -
- )} - -
- - -
- {isInstalling ? ( - - ) : isInstalled ? ( - <> - {hasUpdate && ( - - )} - - - ) : ( - - )} -
-
-
- ); -} diff --git a/packages/editor-app/src/components/PluginPanel.tsx b/packages/editor-app/src/components/PluginPanel.tsx deleted file mode 100644 index 4d6ef7cb..00000000 --- a/packages/editor-app/src/components/PluginPanel.tsx +++ /dev/null @@ -1,256 +0,0 @@ -import { useState, useEffect } from 'react'; -import { EditorPluginManager, IEditorPluginMetadata, EditorPluginCategory } from '@esengine/editor-core'; -import * as LucideIcons from 'lucide-react'; -import { Package, CheckCircle, XCircle, Search, Grid, List, ChevronDown, ChevronRight } from 'lucide-react'; -import '../styles/PluginPanel.css'; - -interface PluginPanelProps { - pluginManager: EditorPluginManager; -} - -const categoryIcons: Record = { - [EditorPluginCategory.Tool]: 'Wrench', - [EditorPluginCategory.Window]: 'LayoutGrid', - [EditorPluginCategory.Inspector]: 'Search', - [EditorPluginCategory.System]: 'Settings', - [EditorPluginCategory.ImportExport]: 'Package' -}; - -const categoryNames: Record = { - [EditorPluginCategory.Tool]: 'Tools', - [EditorPluginCategory.Window]: 'Windows', - [EditorPluginCategory.Inspector]: 'Inspectors', - [EditorPluginCategory.System]: 'System', - [EditorPluginCategory.ImportExport]: 'Import/Export' -}; - -export function PluginPanel({ pluginManager }: PluginPanelProps) { - const [plugins, setPlugins] = useState([]); - const [filter, setFilter] = useState(''); - const [viewMode, setViewMode] = useState<'grid' | 'list'>('list'); - const [expandedCategories, setExpandedCategories] = useState>( - new Set(Object.values(EditorPluginCategory)) - ); - - useEffect(() => { - const updatePlugins = () => { - const allPlugins = pluginManager.getAllPluginMetadata(); - setPlugins(allPlugins); - }; - - updatePlugins(); - }, [pluginManager]); - - const togglePlugin = async (name: string, enabled: boolean) => { - try { - if (enabled) { - await pluginManager.disablePlugin(name); - } else { - await pluginManager.enablePlugin(name); - } - const allPlugins = pluginManager.getAllPluginMetadata(); - setPlugins(allPlugins); - } catch (error) { - console.error(`Failed to toggle plugin ${name}:`, error); - } - }; - - const toggleCategory = (category: EditorPluginCategory) => { - const newExpanded = new Set(expandedCategories); - if (newExpanded.has(category)) { - newExpanded.delete(category); - } else { - newExpanded.add(category); - } - setExpandedCategories(newExpanded); - }; - - const filteredPlugins = plugins.filter((plugin) => { - if (!filter) return true; - const searchLower = filter.toLowerCase(); - return ( - plugin.name.toLowerCase().includes(searchLower) || - plugin.displayName.toLowerCase().includes(searchLower) || - plugin.description?.toLowerCase().includes(searchLower) - ); - }); - - const pluginsByCategory = filteredPlugins.reduce((acc, plugin) => { - if (!acc[plugin.category]) { - acc[plugin.category] = []; - } - acc[plugin.category].push(plugin); - return acc; - }, {} as Record); - - const enabledCount = plugins.filter((p) => p.enabled).length; - const disabledCount = plugins.filter((p) => !p.enabled).length; - - const renderPluginCard = (plugin: IEditorPluginMetadata) => { - const IconComponent = plugin.icon ? (LucideIcons as any)[plugin.icon] : null; - return ( -
-
-
- {IconComponent ? : } -
-
-
{plugin.displayName}
-
v{plugin.version}
-
- -
- {plugin.description && ( -
{plugin.description}
- )} -
- - {(() => { - const CategoryIcon = (LucideIcons as any)[categoryIcons[plugin.category]]; - return CategoryIcon ? : null; - })()} - {categoryNames[plugin.category]} - - {plugin.installedAt && ( - - Installed: {new Date(plugin.installedAt).toLocaleDateString()} - - )} -
-
- ); - }; - - const renderPluginList = (plugin: IEditorPluginMetadata) => { - const IconComponent = plugin.icon ? (LucideIcons as any)[plugin.icon] : null; - return ( -
-
- {IconComponent ? : } -
-
-
- {plugin.displayName} - v{plugin.version} -
- {plugin.description && ( -
{plugin.description}
- )} -
-
- {plugin.enabled ? ( - Enabled - ) : ( - Disabled - )} -
- -
- ); - }; - - return ( -
-
-
-
- - setFilter(e.target.value)} - /> -
-
-
-
- - - {enabledCount} enabled - - - - {disabledCount} disabled - -
-
- - -
-
-
- -
- {plugins.length === 0 ? ( -
- -

No plugins installed

-
- ) : ( -
- {Object.entries(pluginsByCategory).map(([category, categoryPlugins]) => { - const cat = category as EditorPluginCategory; - const isExpanded = expandedCategories.has(cat); - - return ( -
-
toggleCategory(cat)} - > - - - {(() => { - const CategoryIcon = (LucideIcons as any)[categoryIcons[cat]]; - return CategoryIcon ? : null; - })()} - - {categoryNames[cat]} - - {categoryPlugins.length} - -
- - {isExpanded && ( -
- {viewMode === 'grid' - ? categoryPlugins.map(renderPluginCard) - : categoryPlugins.map(renderPluginList)} -
- )} -
- ); - })} -
- )} -
-
- ); -} diff --git a/packages/editor-app/src/components/PluginPublishWizard.tsx b/packages/editor-app/src/components/PluginPublishWizard.tsx deleted file mode 100644 index 816b8472..00000000 --- a/packages/editor-app/src/components/PluginPublishWizard.tsx +++ /dev/null @@ -1,948 +0,0 @@ -import { useState } from 'react'; -import { X, AlertCircle, CheckCircle, Loader, ExternalLink, FolderOpen, FileArchive } from 'lucide-react'; -import { open as openDialog } from '@tauri-apps/plugin-dialog'; -import { GitHubService } from '../services/GitHubService'; -import { GitHubAuth } from './GitHubAuth'; -import { PluginPublishService, type PluginPublishInfo, type PublishProgress } from '../services/PluginPublishService'; -import { PluginBuildService, type BuildProgress } from '../services/PluginBuildService'; -import { PluginSourceParser, type ParsedPluginInfo } from '../services/PluginSourceParser'; -import { open } from '@tauri-apps/plugin-shell'; -import { EditorPluginCategory, type IEditorPluginMetadata } from '@esengine/editor-core'; -import '../styles/PluginPublishWizard.css'; - -interface PluginPublishWizardProps { - githubService: GitHubService; - onClose: () => void; - locale: string; - inline?: boolean; // 是否内联显示(在 tab 中)而不是弹窗 -} - -type Step = 'auth' | 'selectSource' | 'info' | 'building' | 'confirm' | 'publishing' | 'success' | 'error'; - -type SourceType = 'folder' | 'zip'; - -function calculateNextVersion(currentVersion: string): string { - const parts = currentVersion.split('.').map(Number); - if (parts.length !== 3 || parts.some(isNaN)) return currentVersion; - - const [major, minor, patch] = parts; - return `${major}.${minor}.${(patch ?? 0) + 1}`; -} - -export function PluginPublishWizard({ githubService, onClose, locale, inline = false }: PluginPublishWizardProps) { - const [publishService] = useState(() => new PluginPublishService(githubService)); - const [buildService] = useState(() => new PluginBuildService()); - const [sourceParser] = useState(() => new PluginSourceParser()); - - const [step, setStep] = useState(githubService.isAuthenticated() ? 'selectSource' : 'auth'); - const [sourceType, setSourceType] = useState(null); - const [parsedPluginInfo, setParsedPluginInfo] = useState(null); - const [publishInfo, setPublishInfo] = useState>({ - category: 'community', - tags: [] - }); - const [prUrl, setPrUrl] = useState(''); - const [error, setError] = useState(''); - const [buildLog, setBuildLog] = useState([]); - const [buildProgress, setBuildProgress] = useState(null); - const [publishProgress, setPublishProgress] = useState(null); - const [builtZipPath, setBuiltZipPath] = useState(''); - const [existingPR, setExistingPR] = useState<{ number: number; url: string } | null>(null); - const [existingVersions, setExistingVersions] = useState([]); - const [suggestedVersion, setSuggestedVersion] = useState(''); - const [existingManifest, setExistingManifest] = useState(null); - const [isUpdate, setIsUpdate] = useState(false); - - const t = (key: string) => { - const translations: Record> = { - zh: { - title: '发布插件到市场', - updateTitle: '更新插件版本', - stepAuth: '步骤 1: GitHub 登录', - stepSelectSource: '步骤 2: 选择插件源', - stepInfo: '步骤 3: 插件信息', - stepInfoUpdate: '步骤 3: 版本更新', - stepBuilding: '步骤 4: 构建打包', - stepConfirm: '步骤 5: 确认发布', - stepConfirmNoBuilding: '步骤 4: 确认发布', - githubLogin: 'GitHub 登录', - oauthLogin: 'OAuth 登录(推荐)', - tokenLogin: 'Token 登录', - oauthInstructions: '点击下方按钮开始授权:', - oauthStep1: '1. 点击"开始授权"按钮', - oauthStep2: '2. 在浏览器中打开 GitHub 授权页面', - oauthStep3: '3. 输入下方显示的代码并授权', - oauthStep4: '4. 授权完成后会自动跳转', - startAuth: '开始授权', - authorizing: '等待授权中...', - authorized: '授权成功!', - authFailed: '授权失败', - userCode: '授权码', - copyCode: '复制代码', - openBrowser: '打开浏览器', - tokenLabel: 'GitHub Personal Access Token', - tokenPlaceholder: '粘贴你的 GitHub Token', - tokenHint: '需要 repo 和 workflow 权限', - createToken: '创建 Token', - login: '登录', - switchToToken: '使用 Token 登录', - switchToOAuth: '使用 OAuth 登录', - selectSource: '选择插件源', - selectSourceDesc: '选择插件的来源类型', - selectFolder: '选择源代码文件夹', - selectFolderDesc: '选择包含你的插件源代码的文件夹(需要有 package.json,系统将自动构建)', - selectZip: '选择 ZIP 文件', - selectZipDesc: '选择已构建好的插件 ZIP 包(必须包含 package.json 和 dist 目录)', - zipRequirements: 'ZIP 文件要求', - zipStructure: 'ZIP 结构', - zipStructureDetails: 'ZIP 文件必须包含以下内容:', - zipFile1: 'package.json - 插件元数据', - zipFile2: 'dist/ - 构建后的代码目录(包含 index.esm.js)', - zipExample: '示例结构', - zipBuildScript: '打包脚本', - zipBuildScriptDesc: '可以使用以下命令打包:', - recommendFolder: '💡 建议使用"源代码文件夹"方式,系统会自动构建', - browseFolder: '浏览文件夹', - browseZip: '浏览 ZIP 文件', - selectedFolder: '已选择文件夹', - selectedZip: '已选择 ZIP', - sourceTypeFolder: '源代码文件夹', - sourceTypeZip: 'ZIP 文件', - pluginInfo: '插件信息', - version: '版本号', - currentVersion: '当前版本', - suggestedVersion: '建议版本', - versionHistory: '版本历史', - updatePlugin: '更新插件', - newPlugin: '新插件', - category: '分类', - official: '官方', - community: '社区', - repositoryUrl: '仓库地址', - repositoryPlaceholder: 'https://github.com/username/repo', - releaseNotes: '更新说明', - releaseNotesPlaceholder: '描述这个版本的变更...', - tags: '标签(逗号分隔)', - tagsPlaceholder: 'ui, tool, editor', - homepage: '主页 URL(可选)', - next: '下一步', - back: '上一步', - build: '构建并打包', - building: '构建中...', - confirm: '确认发布', - publishing: '发布中...', - publishSuccess: '发布成功!', - publishError: '发布失败', - buildError: '构建失败', - prCreated: 'Pull Request 已创建', - viewPR: '查看 PR', - close: '关闭', - buildingStep1: '正在安装依赖...', - buildingStep2: '正在构建项目...', - buildingStep3: '正在打包 ZIP...', - publishingStep1: '正在 Fork 仓库...', - publishingStep2: '正在创建分支...', - publishingStep3: '正在上传文件...', - publishingStep4: '正在创建 Pull Request...', - confirmMessage: '确认要发布以下插件?', - reviewMessage: '你的插件提交已创建 PR,维护者将进行审核。审核通过后,插件将自动发布到市场。', - existingPRDetected: '检测到现有 PR', - existingPRMessage: '该插件已有待审核的 PR #{{number}}。点击"确认"将更新现有 PR(不会创建新的 PR)。', - viewExistingPR: '查看现有 PR' - }, - en: { - title: 'Publish Plugin to Marketplace', - updateTitle: 'Update Plugin Version', - stepAuth: 'Step 1: GitHub Authentication', - stepSelectSource: 'Step 2: Select Plugin Source', - stepInfo: 'Step 3: Plugin Information', - stepInfoUpdate: 'Step 3: Version Update', - stepBuilding: 'Step 4: Build & Package', - stepConfirm: 'Step 5: Confirm Publication', - stepConfirmNoBuilding: 'Step 4: Confirm Publication', - githubLogin: 'GitHub Login', - oauthLogin: 'OAuth Login (Recommended)', - tokenLogin: 'Token Login', - oauthInstructions: 'Click the button below to start authorization:', - oauthStep1: '1. Click "Start Authorization"', - oauthStep2: '2. Open GitHub authorization page in browser', - oauthStep3: '3. Enter the code shown below and authorize', - oauthStep4: '4. Authorization will complete automatically', - startAuth: 'Start Authorization', - authorizing: 'Waiting for authorization...', - authorized: 'Authorized!', - authFailed: 'Authorization failed', - userCode: 'Authorization Code', - copyCode: 'Copy Code', - openBrowser: 'Open Browser', - tokenLabel: 'GitHub Personal Access Token', - tokenPlaceholder: 'Paste your GitHub Token', - tokenHint: 'Requires repo and workflow permissions', - createToken: 'Create Token', - login: 'Login', - switchToToken: 'Use Token Login', - switchToOAuth: 'Use OAuth Login', - selectSource: 'Select Plugin Source', - selectSourceDesc: 'Choose the plugin source type', - selectFolder: 'Select Source Folder', - selectFolderDesc: 'Select the folder containing your plugin source code (must have package.json, will be built automatically)', - selectZip: 'Select ZIP File', - selectZipDesc: 'Select a pre-built plugin ZIP package (must contain package.json and dist directory)', - zipRequirements: 'ZIP File Requirements', - zipStructure: 'ZIP Structure', - zipStructureDetails: 'The ZIP file must contain:', - zipFile1: 'package.json - Plugin metadata', - zipFile2: 'dist/ - Built code directory (with index.esm.js)', - zipExample: 'Example Structure', - zipBuildScript: 'Build Script', - zipBuildScriptDesc: 'You can use the following commands to package:', - recommendFolder: '💡 Recommended: Use "Source Folder" mode for automatic build', - browseFolder: 'Browse Folder', - browseZip: 'Browse ZIP File', - selectedFolder: 'Selected Folder', - selectedZip: 'Selected ZIP', - sourceTypeFolder: 'Source Folder', - sourceTypeZip: 'ZIP File', - pluginInfo: 'Plugin Information', - version: 'Version', - currentVersion: 'Current Version', - suggestedVersion: 'Suggested Version', - versionHistory: 'Version History', - updatePlugin: 'Update Plugin', - newPlugin: 'New Plugin', - category: 'Category', - official: 'Official', - community: 'Community', - repositoryUrl: 'Repository URL', - repositoryPlaceholder: 'https://github.com/username/repo', - releaseNotes: 'Release Notes', - releaseNotesPlaceholder: 'Describe the changes in this version...', - tags: 'Tags (comma separated)', - tagsPlaceholder: 'ui, tool, editor', - homepage: 'Homepage URL (optional)', - next: 'Next', - back: 'Back', - build: 'Build & Package', - building: 'Building...', - confirm: 'Confirm & Publish', - publishing: 'Publishing...', - publishSuccess: 'Published Successfully!', - publishError: 'Publication Failed', - buildError: 'Build Failed', - prCreated: 'Pull Request Created', - viewPR: 'View PR', - close: 'Close', - buildingStep1: 'Installing dependencies...', - buildingStep2: 'Building project...', - buildingStep3: 'Packaging ZIP...', - publishingStep1: 'Forking repository...', - publishingStep2: 'Creating branch...', - publishingStep3: 'Uploading files...', - publishingStep4: 'Creating Pull Request...', - confirmMessage: 'Confirm publishing this plugin?', - reviewMessage: - 'Your plugin submission has been created as a PR. Maintainers will review it. Once approved, the plugin will be published to the marketplace.', - existingPRDetected: 'Existing PR Detected', - existingPRMessage: 'This plugin already has a pending PR #{{number}}. Clicking "Confirm" will update the existing PR (no new PR will be created).', - viewExistingPR: 'View Existing PR' - } - }; - return translations[locale]?.[key] || translations.en?.[key] || key; - }; - - const handleAuthSuccess = () => { - setStep('selectSource'); - }; - - /** - * 选择并解析插件源(文件夹或 ZIP) - * 统一处理逻辑,避免代码重复 - */ - const handleSelectSource = async (type: SourceType) => { - setError(''); - setSourceType(type); - - try { - let parsedInfo: ParsedPluginInfo; - - if (type === 'folder') { - // 选择文件夹 - const selected = await openDialog({ - directory: true, - multiple: false, - title: t('selectFolder') - }); - - if (!selected) return; - - // 使用 PluginSourceParser 解析文件夹 - parsedInfo = await sourceParser.parseFromFolder(selected as string); - } else { - // 选择 ZIP 文件 - const selected = await openDialog({ - directory: false, - multiple: false, - title: t('selectZip'), - filters: [ - { - name: 'ZIP Files', - extensions: ['zip'] - } - ] - }); - - if (!selected) return; - - // 使用 PluginSourceParser 解析 ZIP - parsedInfo = await sourceParser.parseFromZip(selected as string); - } - - // 验证 package.json - sourceParser.validatePackageJson(parsedInfo.packageJson); - - setParsedPluginInfo(parsedInfo); - - // 检测已发布的版本 - await checkExistingVersions(parsedInfo.packageJson); - - // 检测是否已有待审核的 PR - await checkExistingPR(parsedInfo.packageJson); - - // 进入下一步 - setStep('info'); - } catch (err) { - console.error('[PluginPublishWizard] Failed to parse plugin source:', err); - setError(err instanceof Error ? err.message : 'Failed to parse plugin source'); - } - }; - - /** - * 检测插件是否已发布,获取版本信息 - */ - const checkExistingVersions = async (packageJson: { name: string; version: string }) => { - try { - const pluginId = sourceParser.generatePluginId(packageJson.name); - const manifestContent = await githubService.getFileContent( - 'esengine', - 'ecs-editor-plugins', - `plugins/community/${pluginId}/manifest.json`, - 'main' - ); - const manifest = JSON.parse(manifestContent); - - if (Array.isArray(manifest.versions)) { - const versions = manifest.versions.map((v: any) => v.version); - setExistingVersions(versions); - setExistingManifest(manifest); - setIsUpdate(true); - - // 计算建议版本号 - const latestVersion = manifest.latestVersion || versions[0]; - const suggested = calculateNextVersion(latestVersion); - setSuggestedVersion(suggested); - - // 更新模式:自动填充现有信息 - setPublishInfo((prev) => ({ - ...prev, - version: suggested, - repositoryUrl: manifest.repository?.url || '', - category: manifest.category_type || 'community', - tags: manifest.tags || [], - homepage: manifest.homepage - })); - } else { - // 首次发布 - resetToNewPlugin(packageJson.version); - } - } catch (err) { - console.log('[PluginPublishWizard] No existing versions found, this is a new plugin'); - resetToNewPlugin(packageJson.version); - } - }; - - /** - * 重置为新插件状态 - */ - const resetToNewPlugin = (version: string) => { - setExistingVersions([]); - setExistingManifest(null); - setIsUpdate(false); - setPublishInfo((prev) => ({ - ...prev, - version - })); - }; - - /** - * 检测是否已有待审核的 PR - */ - const checkExistingPR = async (packageJson: { name: string; version: string }) => { - try { - const user = githubService.getUser(); - if (user) { - const branchName = `add-plugin-${packageJson.name}-v${packageJson.version}`; - const headBranch = `${user.login}:${branchName}`; - const pr = await githubService.findPullRequestByBranch('esengine', 'ecs-editor-plugins', headBranch); - if (pr) { - setExistingPR({ number: pr.number, url: pr.html_url }); - } else { - setExistingPR(null); - } - } - } catch (err) { - console.log('[PluginPublishWizard] Failed to check existing PR:', err); - setExistingPR(null); - } - }; - - /** - * 从信息填写步骤进入下一步 - * - 如果是 ZIP,直接跳到确认发布 - * - 如果是文件夹,需要先构建 - */ - const handleNext = () => { - if (!publishInfo.version || !publishInfo.repositoryUrl || !publishInfo.releaseNotes) { - setError('Please fill in all required fields'); - return; - } - - if (!parsedPluginInfo) { - setError('Plugin source not selected'); - return; - } - - // ZIP 文件已经构建好,直接跳到确认步骤 - if (parsedPluginInfo.sourceType === 'zip' && parsedPluginInfo.zipPath) { - setBuiltZipPath(parsedPluginInfo.zipPath); - setStep('confirm'); - } else { - // 文件夹需要构建 - setStep('building'); - handleBuild(); - } - }; - - /** - * 构建插件(仅对文件夹源有效) - */ - const handleBuild = async () => { - if (!parsedPluginInfo || parsedPluginInfo.sourceType !== 'folder') { - setError('Cannot build: plugin source is not a folder'); - setStep('error'); - return; - } - - setBuildLog([]); - setBuildProgress(null); - setError(''); - - buildService.setProgressCallback((progress) => { - console.log('[PluginPublishWizard] Build progress:', progress); - setBuildProgress(progress); - - if (progress.step === 'install') { - setBuildLog((prev) => { - if (prev[prev.length - 1] !== t('buildingStep1')) { - return [...prev, t('buildingStep1')]; - } - return prev; - }); - } else if (progress.step === 'build') { - setBuildLog((prev) => { - if (prev[prev.length - 1] !== t('buildingStep2')) { - return [...prev, t('buildingStep2')]; - } - return prev; - }); - } else if (progress.step === 'package') { - setBuildLog((prev) => { - if (prev[prev.length - 1] !== t('buildingStep3')) { - return [...prev, t('buildingStep3')]; - } - return prev; - }); - } else if (progress.step === 'complete') { - setBuildLog((prev) => [...prev, t('buildComplete')]); - } - - if (progress.output) { - console.log('[Build output]', progress.output); - } - }); - - try { - const zipPath = await buildService.buildPlugin(parsedPluginInfo.sourcePath); - console.log('[PluginPublishWizard] Build completed, ZIP at:', zipPath); - setBuiltZipPath(zipPath); - setStep('confirm'); - } catch (err) { - console.error('[PluginPublishWizard] Build failed:', err); - setError(err instanceof Error ? err.message : 'Unknown error'); - setStep('error'); - } - }; - - /** - * 发布插件到市场 - */ - const handlePublish = async () => { - setStep('publishing'); - setError(''); - setPublishProgress(null); - - // 设置进度回调 - publishService.setProgressCallback((progress) => { - setPublishProgress(progress); - }); - - try { - // 验证必填字段 - if (!publishInfo.version || !publishInfo.repositoryUrl || !publishInfo.releaseNotes) { - throw new Error('Missing required fields'); - } - - // 验证插件源 - if (!parsedPluginInfo) { - throw new Error('Plugin source not selected'); - } - - // 验证 ZIP 路径 - if (!builtZipPath) { - throw new Error('Plugin ZIP file not available'); - } - - const { packageJson } = parsedPluginInfo; - - const pluginMetadata: IEditorPluginMetadata = { - name: packageJson.name, - displayName: packageJson.description || packageJson.name, - description: packageJson.description || '', - version: packageJson.version, - category: EditorPluginCategory.Tool, - icon: 'Package', - enabled: true, - installedAt: Date.now() - }; - - const fullPublishInfo: PluginPublishInfo = { - pluginMetadata, - version: publishInfo.version || packageJson.version, - releaseNotes: publishInfo.releaseNotes || '', - repositoryUrl: publishInfo.repositoryUrl || '', - category: publishInfo.category || 'community', - tags: publishInfo.tags, - homepage: publishInfo.homepage, - screenshots: publishInfo.screenshots - }; - - console.log('[PluginPublishWizard] Publishing with info:', fullPublishInfo); - console.log('[PluginPublishWizard] Built ZIP path:', builtZipPath); - - const prUrl = await publishService.publishPlugin(fullPublishInfo, builtZipPath); - setPrUrl(prUrl); - setStep('success'); - } catch (err) { - console.error('[PluginPublishWizard] Publish failed:', err); - setError(err instanceof Error ? err.message : 'Unknown error'); - setStep('error'); - } - }; - - const openPR = async () => { - if (prUrl) { - await open(prUrl); - } - }; - - const wizardContent = ( -
inline ? undefined : e.stopPropagation()}> -
-

{t('title')}

- {!inline && ( - - )} -
- -
- {step === 'auth' && ( -
-

{t('stepAuth')}

- -
- )} - - {step === 'selectSource' && ( -
-

{t('stepSelectSource')}

-

{t('selectSourceDesc')}

- -
- - - -
- - {/* ZIP 文件要求说明 */} -
- - - {t('zipRequirements')} - -
-
-

{t('zipStructure')}

-

{t('zipStructureDetails')}

-
    -
  • package.json - {t('zipFile1')}
  • -
  • dist/ - {t('zipFile2')}
  • -
-
- -
-

{t('zipBuildScript')}

-

{t('zipBuildScriptDesc')}

-
-                                        {`npm install
-npm run build
-# 然后将 package.json 和 dist/ 目录一起压缩为 ZIP
-# ZIP 结构:
-#   plugin.zip
-#   ├── package.json
-#   └── dist/
-#       └── index.esm.js`}
-                                    
-
- -
- {t('recommendFolder')} -
-
-
- - {parsedPluginInfo && ( -
- {parsedPluginInfo.sourceType === 'folder' ? ( - - ) : ( - - )} -
- {parsedPluginInfo.sourcePath} - {parsedPluginInfo.packageJson.name} v{parsedPluginInfo.packageJson.version} -
-
- )} - - {error && ( -
- - {error} -
- )} - - {parsedPluginInfo && ( -
- -
- )} -
- )} - - {step === 'info' && ( -
-

{t('stepInfo')}

- - {existingPR && ( -
- -
- {t('existingPRDetected')} -

{t('existingPRMessage').replace('{{number}}', String(existingPR.number))}

- -
-
- )} - -
- - {isUpdate && ( -
-
- - {t('updatePlugin')}: {existingManifest?.name} v{existingVersions[0]} -
- {suggestedVersion && ( - - )} -
- )} - setPublishInfo({ ...publishInfo, version: e.target.value })} - placeholder="1.0.0" - /> - {isUpdate && ( -
- {t('versionHistory')} ({existingVersions.length}) -
    - {existingVersions.map((v) => ( -
  • v{v}
  • - ))} -
-
- )} -
- -
- -