import React, { useState, useEffect, useRef, useCallback } from 'react'; import * as ReactDOM from 'react-dom'; import * as ReactJSXRuntime from 'react/jsx-runtime'; import { Core, createLogger, Scene } from '@esengine/ecs-framework'; import * as ECSFramework from '@esengine/ecs-framework'; import { getProfilerService } from './services/getService'; // 将 React 暴露到全局,供动态加载的插件使用 // editor-runtime.js 将 React 设为 external,需要从全局获取 (window as any).React = React; (window as any).ReactDOM = ReactDOM; (window as any).ReactJSXRuntime = ReactJSXRuntime; import { PluginManager, UIRegistry, MessageHub, EntityStoreService, ComponentRegistry, LocaleService, LogService, SettingsRegistry, SceneManagerService, ProjectService, CompilerRegistry, ICompilerRegistry, InspectorRegistry, INotification, CommandManager, BuildService } from '@esengine/editor-core'; import type { IDialogExtended } from './services/TauriDialogService'; import { GlobalBlackboardService } from '@esengine/behavior-tree'; import { ServiceRegistry, PluginInstaller, useDialogStore } from './app/managers'; import { StartupPage } from './components/StartupPage'; import { ProjectCreationWizard } from './components/ProjectCreationWizard'; import { SceneHierarchy } from './components/SceneHierarchy'; import { Inspector } from './components/inspectors/Inspector'; import { AssetBrowser } from './components/AssetBrowser'; import { Viewport } from './components/Viewport'; import { AdvancedProfilerWindow } from './components/AdvancedProfilerWindow'; import { PortManager } from './components/PortManager'; import { SettingsWindow } from './components/SettingsWindow'; import { AboutDialog } from './components/AboutDialog'; import { ErrorDialog } from './components/ErrorDialog'; import { ConfirmDialog } from './components/ConfirmDialog'; import { PluginGeneratorWindow } from './components/PluginGeneratorWindow'; import { BuildSettingsWindow } from './components/BuildSettingsWindow'; import { ForumPanel } from './components/forum'; import { ToastProvider, useToast } from './components/Toast'; import { TitleBar } from './components/TitleBar'; import { MainToolbar } from './components/MainToolbar'; import { FlexLayoutDockContainer, FlexDockPanel } from './components/FlexLayoutDockContainer'; import { StatusBar } from './components/StatusBar'; import { TauriAPI } from './api/tauri'; import { SettingsService } from './services/SettingsService'; import { PluginLoader } from './services/PluginLoader'; import { EngineService } from './services/EngineService'; import { CompilerConfigDialog } from './components/CompilerConfigDialog'; import { checkForUpdatesOnStartup } from './utils/updater'; import { useLocale } from './hooks/useLocale'; import { en, zh, es } from './locales'; import type { Locale } from '@esengine/editor-core'; import { Loader2 } from 'lucide-react'; import './styles/App.css'; const coreInstance = Core.create({ debug: true }); const localeService = new LocaleService(); localeService.registerTranslations('en', en); localeService.registerTranslations('zh', zh); localeService.registerTranslations('es', es); Core.services.registerInstance(LocaleService, localeService); Core.services.registerSingleton(GlobalBlackboardService); Core.services.registerSingleton(CompilerRegistry); // 在 CompilerRegistry 实例化后,也用 Symbol 注册,用于跨包插件访问 // 注意:registerSingleton 会延迟实例化,所以需要在第一次使用后再注册 Symbol const compilerRegistryInstance = Core.services.resolve(CompilerRegistry); Core.services.registerInstance(ICompilerRegistry, compilerRegistryInstance); const logger = createLogger('App'); function App() { const initRef = useRef(false); const [pluginLoader] = useState(() => new PluginLoader()); 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 [entityStore, setEntityStore] = useState(null); const [messageHub, setMessageHub] = useState(null); const [inspectorRegistry, setInspectorRegistry] = useState(null); const [logService, setLogService] = useState(null); const [uiRegistry, setUiRegistry] = useState(null); const [settingsRegistry, setSettingsRegistry] = useState(null); const [sceneManager, setSceneManager] = useState(null); const [notification, setNotification] = useState(null); const [dialog, setDialog] = useState(null); const [buildService, setBuildService] = useState(null); const [commandManager] = useState(() => new CommandManager()); const { t, locale, changeLocale } = useLocale(); // 同步 locale 到 TauriDialogService useEffect(() => { if (dialog) { dialog.setLocale(locale); } }, [locale, dialog]); const [status, setStatus] = useState(t('header.status.initializing')); const [panels, setPanels] = useState([]); const [pluginUpdateTrigger, setPluginUpdateTrigger] = useState(0); const [isRemoteConnected, setIsRemoteConnected] = useState(false); const [showProjectWizard, setShowProjectWizard] = useState(false); const { showProfiler, setShowProfiler, showAdvancedProfiler, setShowAdvancedProfiler, showPortManager, setShowPortManager, showSettings, setShowSettings, showAbout, setShowAbout, showPluginGenerator, setShowPluginGenerator, showBuildSettings, setShowBuildSettings, 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 [compilerDialog, setCompilerDialog] = useState<{ isOpen: boolean; compilerId: string; currentFileName?: string; }>({ isOpen: false, compilerId: '' }); useEffect(() => { // 禁用默认右键菜单 const handleContextMenu = (e: MouseEvent) => { e.preventDefault(); }; document.addEventListener('contextmenu', handleContextMenu); return () => { document.removeEventListener('contextmenu', handleContextMenu); }; }, []); // 快捷键监听 useEffect(() => { const handleKeyDown = async (e: KeyboardEvent) => { if (e.ctrlKey || e.metaKey) { switch (e.key.toLowerCase()) { case 's': e.preventDefault(); if (sceneManager) { try { await sceneManager.saveScene(); const sceneState = sceneManager.getSceneState(); showToast(t('scene.savedSuccess', { name: sceneState.sceneName }), 'success'); } catch (error) { console.error('Failed to save scene:', error); showToast(t('scene.saveFailed'), 'error'); } } break; case 'r': e.preventDefault(); handleReloadPlugins(); break; } } }; document.addEventListener('keydown', handleKeyDown); return () => { document.removeEventListener('keydown', handleKeyDown); }; }, [sceneManager, locale, currentProjectPath, pluginManager]); useEffect(() => { if (messageHub) { const unsubscribeEnabled = messageHub.subscribe('plugin:enabled', () => { setPluginUpdateTrigger((prev) => prev + 1); }); const unsubscribeDisabled = messageHub.subscribe('plugin:disabled', () => { setPluginUpdateTrigger((prev) => prev + 1); }); const unsubscribeNotification = messageHub.subscribe('notification:show', (notification: { message: string; type: 'success' | 'error' | 'warning' | 'info'; timestamp: number }) => { if (notification && notification.message) { showToast(notification.message, notification.type); } }); return () => { unsubscribeEnabled(); unsubscribeDisabled(); unsubscribeNotification(); }; } }, [messageHub, showToast]); // 监听远程连接状态 // Monitor remote connection status useEffect(() => { const checkConnection = () => { const profilerService = getProfilerService(); const connected = !!(profilerService && profilerService.isConnected()); setIsRemoteConnected((prevConnected) => { if (connected !== prevConnected) { // 状态发生变化 | State has changed if (connected) { setStatus(t('header.status.remoteConnected')); } else { if (projectLoaded) { const componentRegistry = Core.services.resolve(ComponentRegistry); const componentCount = componentRegistry?.getAllComponents().length || 0; setStatus(t('header.status.projectOpened') + (componentCount > 0 ? ` (${componentCount} components registered)` : '')); } else { setStatus(t('header.status.ready')); } } return connected; } return prevConnected; }); }; const interval = setInterval(checkConnection, 1000); return () => clearInterval(interval); }, [projectLoaded, t]); useEffect(() => { const initializeEditor = async () => { // 使用 ref 防止 React StrictMode 的双重调用 if (initRef.current) { return; } initRef.current = true; try { // ECS Framework 已通过 PluginSDKRegistry 暴露到全局 // ECS Framework is exposed globally via PluginSDKRegistry const editorScene = new Scene(); Core.setScene(editorScene); const serviceRegistry = new ServiceRegistry(); const services = serviceRegistry.registerAllServices(coreInstance); serviceRegistry.setupRemoteLogListener(services.logService); 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); services.messageHub.subscribe('ui:openWindow', (data: any) => { const { windowId } = data; if (windowId === 'profiler') { setShowProfiler(true); } else if (windowId === 'advancedProfiler') { setShowAdvancedProfiler(true); } else if (windowId === 'pluginManager') { // 插件管理现在整合到设置窗口中 setSettingsInitialCategory('plugins'); setShowSettings(true); } }); setInitialized(true); setPluginManager(services.pluginManager); setEntityStore(services.entityStore); setMessageHub(services.messageHub); setInspectorRegistry(services.inspectorRegistry); setLogService(services.logService); setUiRegistry(services.uiRegistry); setSettingsRegistry(services.settingsRegistry); setSceneManager(services.sceneManager); setNotification(services.notification); setDialog(services.dialog as IDialogExtended); setBuildService(services.buildService); setStatus(t('header.status.ready')); // Check for updates on startup (after 3 seconds) checkForUpdatesOnStartup(); } catch (error) { console.error('Failed to initialize editor:', error); setStatus(t('header.status.failed')); } }; initializeEditor(); }, []); useEffect(() => { if (!messageHub) return; const unsubscribe = messageHub.subscribe('dynamic-panel:open', (data: any) => { const { panelId, title } = data; logger.info('Opening dynamic panel:', panelId, 'with title:', title); setActiveDynamicPanels((prev) => { const newPanels = prev.includes(panelId) ? prev : [...prev, panelId]; return newPanels; }); setActivePanelId(panelId); // 更新动态面板标题 if (title) { setDynamicPanelTitles((prev) => { const newTitles = new Map(prev); newTitles.set(panelId, title); return newTitles; }); } }); return () => unsubscribe?.(); }, [messageHub]); useEffect(() => { if (!messageHub) return; const unsubscribe = messageHub.subscribe('editor:fullscreen', (data: any) => { const { fullscreen } = data; logger.info('Editor fullscreen state changed:', fullscreen); setIsEditorFullscreen(fullscreen); }); return () => unsubscribe?.(); }, [messageHub]); useEffect(() => { if (!messageHub) return; const unsubscribe = messageHub.subscribe('compiler:open-dialog', (data: { compilerId: string; currentFileName?: string; projectPath?: string; }) => { logger.info('Opening compiler dialog:', data.compilerId); setCompilerDialog({ isOpen: true, compilerId: data.compilerId, currentFileName: data.currentFileName }); }); return () => unsubscribe?.(); }, [messageHub]); const handleOpenRecentProject = async (projectPath: string) => { try { setIsLoading(true); setLoadingMessage(t('loading.step1')); const projectService = Core.services.resolve(ProjectService); if (!projectService) { console.error('Required services not available'); setIsLoading(false); return; } await projectService.openProject(projectPath); // 注意:插件配置会在引擎初始化后加载和激活 // Note: Plugin config will be loaded and activated after engine initialization // 设置 Tauri project:// 协议的基础路径(用于加载插件等项目文件) await TauriAPI.setProjectBasePath(projectPath); // 更新项目 tsconfig,直接引用引擎类型定义 // Update project tsconfig to reference engine type definitions directly try { await TauriAPI.updateProjectTsconfig(projectPath); } catch (e) { console.warn('[App] Failed to update project tsconfig:', e); } const settings = SettingsService.getInstance(); settings.addRecentProject(projectPath); setCurrentProjectPath(projectPath); // 设置 projectLoaded 为 true,触发主界面渲染(包括 Viewport) setProjectLoaded(true); // 等待引擎初始化完成(Viewport 渲染后会触发引擎初始化) setLoadingMessage(t('loading.step2')); const engineService = EngineService.getInstance(); // 等待引擎初始化(最多等待 30 秒,因为需要等待 Viewport 渲染) const engineReady = await engineService.waitForInitialization(30000); if (!engineReady) { throw new Error(t('loading.engineTimeoutError')); } // 加载项目插件配置并激活插件(在引擎初始化后、模块系统初始化前) // Load project plugin config and activate plugins (after engine init, before module system init) if (pluginManager) { const pluginSettings = projectService.getPluginSettings(); console.log('[App] Plugin settings from project:', pluginSettings); if (pluginSettings && pluginSettings.enabledPlugins.length > 0) { console.log('[App] Loading plugin config:', pluginSettings.enabledPlugins); await pluginManager.loadConfig({ enabledPlugins: pluginSettings.enabledPlugins }); } else { console.log('[App] No plugin settings found in project config'); } } // 初始化模块系统(所有插件的 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(t('loading.step3')); const sceneManagerService = Core.services.resolve(SceneManagerService); if (sceneManagerService) { await sceneManagerService.newScene(); } if (pluginManager) { setLoadingMessage(t('loading.loadingPlugins')); await pluginLoader.loadProjectPlugins(projectPath, pluginManager); } setIsLoading(false); } catch (error) { console.error('Failed to open project:', error); setStatus(t('header.status.failed')); setIsLoading(false); const errorMessage = error instanceof Error ? error.message : String(error); setErrorDialog({ title: t('project.openFailed'), message: `${t('project.openFailed')}:\n${errorMessage}` }); } }; const handleOpenProject = async () => { try { const projectPath = await TauriAPI.openProjectDialog(); if (!projectPath) return; await handleOpenRecentProject(projectPath); } catch (error) { console.error('Failed to open project dialog:', error); } }; const handleCreateProject = () => { setShowProjectWizard(true); }; const handleCreateProjectFromWizard = async (projectName: string, projectPath: string, _templateId: string) => { // 使用与 projectPath 相同的路径分隔符 | Use same separator as projectPath const sep = projectPath.includes('/') ? '/' : '\\'; const fullProjectPath = `${projectPath}${sep}${projectName}`; try { setIsLoading(true); setLoadingMessage(t('project.creating')); const projectService = Core.services.resolve(ProjectService); if (!projectService) { console.error('ProjectService not available'); setIsLoading(false); setErrorDialog({ title: t('project.createFailed'), message: t('project.serviceUnavailable') }); return; } await projectService.createProject(fullProjectPath); setLoadingMessage(t('project.createdOpening')); await handleOpenRecentProject(fullProjectPath); } catch (error) { console.error('Failed to create project:', error); setIsLoading(false); const errorMessage = error instanceof Error ? error.message : String(error); if (errorMessage.includes('already exists')) { setConfirmDialog({ title: t('project.alreadyExists'), message: t('project.existsQuestion'), confirmText: t('project.open'), cancelText: t('common.cancel'), onConfirm: () => { setConfirmDialog(null); setIsLoading(true); setLoadingMessage(t('project.opening')); handleOpenRecentProject(fullProjectPath).catch((err) => { console.error('Failed to open project:', err); setIsLoading(false); setErrorDialog({ title: t('project.openFailed'), message: `${t('project.openFailed')}:\n${err instanceof Error ? err.message : String(err)}` }); }); } }); } else { setStatus(t('project.createFailed')); setErrorDialog({ title: t('project.createFailed'), message: `${t('project.createFailed')}:\n${errorMessage}` }); } } }; const handleBrowseProjectPath = async (): Promise => { try { const path = await TauriAPI.openProjectDialog(); return path || null; } catch (error) { console.error('Failed to browse path:', error); return null; } }; const handleNewScene = async () => { if (!sceneManager) { console.error('SceneManagerService not available'); return; } try { await sceneManager.newScene(); setStatus(t('scene.newCreated')); } catch (error) { console.error('Failed to create new scene:', error); setStatus(t('scene.createFailed')); } }; const handleOpenScene = async () => { if (!sceneManager) { console.error('SceneManagerService not available'); return; } try { await sceneManager.openScene(); const sceneState = sceneManager.getSceneState(); setStatus(t('scene.openedSuccess', { name: sceneState.sceneName })); } catch (error) { console.error('Failed to open scene:', error); setStatus(t('scene.openFailed')); } }; const handleOpenSceneByPath = useCallback(async (scenePath: string) => { if (!sceneManager) { console.error('SceneManagerService not available'); return; } try { await sceneManager.openScene(scenePath); const sceneState = sceneManager.getSceneState(); setStatus(t('scene.openedSuccess', { name: sceneState.sceneName })); } catch (error) { console.error('Failed to open scene:', error); setStatus(t('scene.openFailed')); setErrorDialog({ title: t('scene.openFailed'), message: `${t('scene.openFailed')}:\n${error instanceof Error ? error.message : String(error)}` }); } }, [sceneManager, locale]); const handleSaveScene = async () => { if (!sceneManager) { console.error('SceneManagerService not available'); return; } try { await sceneManager.saveScene(); const sceneState = sceneManager.getSceneState(); setStatus(t('scene.savedSuccess', { name: sceneState.sceneName })); } catch (error) { console.error('Failed to save scene:', error); setStatus(t('scene.saveFailed')); } }; const handleSaveSceneAs = async () => { if (!sceneManager) { console.error('SceneManagerService not available'); return; } try { await sceneManager.saveSceneAs(); const sceneState = sceneManager.getSceneState(); setStatus(t('scene.savedSuccess', { name: sceneState.sceneName })); } catch (error) { console.error('Failed to save scene as:', error); setStatus(t('scene.saveAsFailed')); } }; const handleCloseProject = async () => { // 卸载项目插件 if (pluginManager) { await pluginLoader.unloadProjectPlugins(pluginManager); } // 清理场景(会清理所有实体和系统) // Clear scene (clears all entities and systems) const scene = Core.scene; if (scene) { scene.end(); } // 清理模块系统 const engineService = EngineService.getInstance(); engineService.clearModuleSystems(); // 关闭 ProjectService 中的项目 const projectService = Core.services.tryResolve(ProjectService); if (projectService) { await projectService.closeProject(); } setProjectLoaded(false); setCurrentProjectPath(null); setStatus(t('header.status.ready')); }; const handleExit = () => { window.close(); }; const handleLocaleChange = (newLocale: Locale) => { changeLocale(newLocale); // 通知所有已加载的插件更新语言 if (pluginManager) { pluginManager.setLocale(newLocale); // 通过 MessageHub 通知需要重新获取节点模板 if (messageHub) { messageHub.publish('locale:changed', { locale: newLocale }); } } }; const handleToggleDevtools = async () => { try { await TauriAPI.toggleDevtools(); } catch (error) { console.error('Failed to toggle devtools:', error); } }; const handleOpenAbout = () => { setShowAbout(true); }; const handleCreatePlugin = () => { setShowPluginGenerator(true); }; const handleReloadPlugins = async () => { if (currentProjectPath && pluginManager) { try { // 1. 关闭所有动态面板 setActiveDynamicPanels([]); // 2. 清空当前面板列表(强制卸载插件面板组件) setPanels((prev) => prev.filter((p) => ['scene-hierarchy', 'inspector', 'console', 'asset-browser'].includes(p.id) )); // 3. 等待React完成卸载 await new Promise((resolve) => setTimeout(resolve, 200)); // 4. 卸载所有项目插件(清理UIRegistry、调用uninstall) await pluginLoader.unloadProjectPlugins(pluginManager); // 5. 等待卸载完成 await new Promise((resolve) => setTimeout(resolve, 100)); // 6. 重新加载插件 await pluginLoader.loadProjectPlugins(currentProjectPath, pluginManager); // 7. 触发面板重新渲染 setPluginUpdateTrigger((prev) => prev + 1); showToast(t('plugin.reloadedSuccess'), 'success'); } catch (error) { console.error('Failed to reload plugins:', error); showToast(t('plugin.reloadFailed'), 'error'); } } }; useEffect(() => { if (projectLoaded && entityStore && messageHub && logService && uiRegistry && pluginManager) { const corePanels: FlexDockPanel[] = [ { id: 'scene-hierarchy', title: t('panel.sceneHierarchy'), content: , closable: false }, { id: 'viewport', title: t('panel.viewport'), content: , closable: false }, { id: 'inspector', title: t('panel.inspector'), content: , closable: false }, { id: 'forum', title: t('panel.forum'), content: , closable: true } ]; // 获取启用的插件面板 const pluginPanels: FlexDockPanel[] = uiRegistry.getAllPanels() .filter((panelDesc) => { if (!panelDesc.component) { return false; } if (panelDesc.isDynamic) { return false; } return true; }) .map((panelDesc) => { const Component = panelDesc.component; // 使用 titleKey 翻译,回退到 title // Use titleKey for translation, fallback to title const title = panelDesc.titleKey ? t(panelDesc.titleKey) : panelDesc.title; return { id: panelDesc.id, title, content: , closable: panelDesc.closable ?? true }; }); // 添加激活的动态面板 const dynamicPanels: FlexDockPanel[] = activeDynamicPanels .filter((panelId) => { const panelDesc = uiRegistry.getPanel(panelId); return panelDesc && (panelDesc.component || panelDesc.render); }) .map((panelId) => { const panelDesc = uiRegistry.getPanel(panelId)!; // 优先使用动态标题,否则使用默认标题 // Prefer dynamic title, fallback to default title const customTitle = dynamicPanelTitles.get(panelId); // 使用 titleKey 翻译,回退到 title const defaultTitle = panelDesc.titleKey ? t(panelDesc.titleKey) : panelDesc.title; // 支持 component 或 render 两种方式 let content: React.ReactNode; if (panelDesc.component) { const Component = panelDesc.component; content = ; } else if (panelDesc.render) { content = panelDesc.render(); } return { id: panelDesc.id, title: customTitle || defaultTitle, content, closable: panelDesc.closable ?? true }; }); setPanels([...corePanels, ...pluginPanels, ...dynamicPanels]); } }, [projectLoaded, entityStore, messageHub, logService, uiRegistry, pluginManager, locale, currentProjectPath, t, pluginUpdateTrigger, handleOpenSceneByPath, activeDynamicPanels, dynamicPanelTitles]); if (!initialized) { return (

Loading Editor...

); } if (!projectLoaded) { const settings = SettingsService.getInstance(); const recentProjects = settings.getRecentProjects(); return ( <> { settings.removeRecentProject(projectPath); // 强制重新渲染 | Force re-render setStatus(t('header.status.ready')); }} onDeleteProject={async (projectPath) => { console.log('[App] onDeleteProject called with path:', projectPath); try { console.log('[App] Calling TauriAPI.deleteFolder...'); await TauriAPI.deleteFolder(projectPath); console.log('[App] deleteFolder succeeded'); // 删除成功后从列表中移除并触发重新渲染 // Remove from list and trigger re-render after successful deletion settings.removeRecentProject(projectPath); setStatus(t('header.status.ready')); } catch (error) { console.error('[App] Failed to delete project:', error); setErrorDialog({ title: t('project.deleteFailed'), message: `${t('project.deleteFailed')}:\n${error instanceof Error ? error.message : String(error)}` }); } }} onLocaleChange={handleLocaleChange} recentProjects={recentProjects} /> setShowProjectWizard(false)} onCreateProject={handleCreateProjectFromWizard} onBrowsePath={handleBrowseProjectPath} locale={locale} /> {isLoading && (

{loadingMessage}

)} {errorDialog && ( setErrorDialog(null)} /> )} {confirmDialog && ( { confirmDialog.onConfirm(); setConfirmDialog(null); }} onCancel={() => { if (confirmDialog.onCancel) { confirmDialog.onCancel(); } setConfirmDialog(null); }} /> )} ); } const projectName = currentProjectPath ? currentProjectPath.split(/[\\/]/).pop() : 'Untitled'; return (
{!isEditorFullscreen && ( <> { setSettingsInitialCategory('plugins'); setShowSettings(true); }} onOpenProfiler={() => setShowProfiler(true)} onOpenPortManager={() => setShowPortManager(true)} onOpenSettings={() => setShowSettings(true)} onToggleDevtools={handleToggleDevtools} onOpenAbout={handleOpenAbout} onCreatePlugin={handleCreatePlugin} onReloadPlugins={handleReloadPlugins} onOpenBuildSettings={() => setShowBuildSettings(true)} /> )} setCompilerDialog({ isOpen: false, compilerId: '' })} onCompileComplete={(result) => { if (result.success) { showToast(result.message, 'success'); } else { showToast(result.message, 'error'); } }} />
{ logger.info('Panel closed:', panelId); setActiveDynamicPanels((prev) => prev.filter((id) => id !== panelId)); }} />
{(showProfiler || showAdvancedProfiler) && ( { setShowProfiler(false); setShowAdvancedProfiler(false); }} /> )} {showPortManager && ( setShowPortManager(false)} /> )} {showSettings && settingsRegistry && ( { setShowSettings(false); setSettingsInitialCategory(undefined); }} settingsRegistry={settingsRegistry} initialCategoryId={settingsInitialCategory} /> )} {showAbout && ( setShowAbout(false)} /> )} {showPluginGenerator && ( setShowPluginGenerator(false)} projectPath={currentProjectPath} onSuccess={async () => { if (currentProjectPath && pluginManager) { await pluginLoader.loadProjectPlugins(currentProjectPath, pluginManager); } }} /> )} {showBuildSettings && ( setShowBuildSettings(false)} projectPath={currentProjectPath || undefined} buildService={buildService || undefined} sceneManager={sceneManager || undefined} /> )} {errorDialog && ( setErrorDialog(null)} /> )} {confirmDialog && ( { confirmDialog.onConfirm(); setConfirmDialog(null); }} onCancel={() => { if (confirmDialog.onCancel) { confirmDialog.onCancel(); } setConfirmDialog(null); }} /> )}
); } function AppWithToast() { return ( ); } export default AppWithToast;