import { useState, useEffect, useRef, useCallback } from 'react'; import { Core, IService, ServiceType } from '@esengine/ecs-framework'; import { CompilerRegistry, ICompiler, CompilerContext, CompileResult, IFileSystem, IDialog, FileEntry } from '@esengine/editor-core'; import { X, Play, Loader2 } from 'lucide-react'; import { open as tauriOpen, save as tauriSave, message as tauriMessage, confirm as tauriConfirm } from '@tauri-apps/plugin-dialog'; import { invoke } from '@tauri-apps/api/core'; import '../styles/CompilerConfigDialog.css'; interface DirectoryEntry { name: string; path: string; is_dir: boolean; } interface CompilerConfigDialogProps { isOpen: boolean; compilerId: string; projectPath: string | null; currentFileName?: string; onClose: () => void; onCompileComplete?: (result: CompileResult) => void; } export const CompilerConfigDialog: React.FC = ({ isOpen, compilerId, projectPath, currentFileName, onClose, onCompileComplete }) => { const [compiler, setCompiler] = useState(null); const [options, setOptions] = useState(null); const [isCompiling, setIsCompiling] = useState(false); const [compileResult, setCompileResult] = useState(null); const optionsRef = useRef(null); useEffect(() => { if (isOpen && compilerId) { try { const registry = Core.services.resolve(CompilerRegistry); console.log('[CompilerConfigDialog] Registry resolved:', registry); console.log('[CompilerConfigDialog] Available compilers:', registry.getAll().map(c => c.id)); const comp = registry.get(compilerId); console.log(`[CompilerConfigDialog] Looking for compiler: ${compilerId}, found:`, comp); setCompiler(comp || null); } catch (error) { console.error('[CompilerConfigDialog] Failed to resolve CompilerRegistry:', error); setCompiler(null); } } }, [isOpen, compilerId]); const handleOptionsChange = useCallback((newOptions: unknown) => { optionsRef.current = newOptions; setOptions(newOptions); }, []); const createFileSystem = (): IFileSystem => ({ readFile: async (path: string) => { return await invoke('read_file_content', { path }); }, writeFile: async (path: string, content: string) => { await invoke('write_file_content', { path, content }); }, writeBinary: async (path: string, data: Uint8Array) => { await invoke('write_binary_file', { filePath: path, content: Array.from(data) }); }, exists: async (path: string) => { return await invoke('path_exists', { path }); }, createDirectory: async (path: string) => { await invoke('create_directory', { path }); }, listDirectory: async (path: string): Promise => { const entries = await invoke('list_directory', { path }); return entries.map(e => ({ name: e.name, path: e.path, isDirectory: e.is_dir })); }, deleteFile: async (path: string) => { await invoke('delete_file', { path }); }, deleteDirectory: async (path: string) => { await invoke('delete_folder', { path }); }, scanFiles: async (dir: string, pattern: string) => { // Check if directory exists, create if not const dirExists = await invoke('path_exists', { path: dir }); if (!dirExists) { await invoke('create_directory', { path: dir }); return []; // New directory has no files } const entries = await invoke('list_directory', { path: dir }); const ext = pattern.replace(/\*/g, ''); return entries .filter(e => !e.is_dir && e.name.endsWith(ext)) .map(e => e.name.replace(ext, '')); } }); const createDialog = (): IDialog => ({ openDialog: async (opts) => { const result = await tauriOpen({ directory: opts.directory, multiple: opts.multiple, title: opts.title, defaultPath: opts.defaultPath }); return result; }, saveDialog: async (opts) => { const result = await tauriSave({ title: opts.title, defaultPath: opts.defaultPath, filters: opts.filters }); return result; }, showMessage: async (title: string, message: string, type?: 'info' | 'warning' | 'error') => { await tauriMessage(message, { title, kind: type || 'info' }); }, showConfirm: async (title: string, message: string) => { return await tauriConfirm(message, { title }); } }); const createContext = (): CompilerContext => ({ projectPath, moduleContext: { fileSystem: createFileSystem(), dialog: createDialog() }, getService: (serviceClass: ServiceType): T | undefined => { try { return Core.services.resolve(serviceClass); } catch { return undefined; } } }); const handleCompile = async () => { if (!compiler || !optionsRef.current) return; setIsCompiling(true); setCompileResult(null); try { const context = createContext(); const result = await compiler.compile(optionsRef.current, context); setCompileResult(result); onCompileComplete?.(result); if (result.success) { setTimeout(() => { onClose(); }, 2000); } } catch (error) { setCompileResult({ success: false, message: `编译失败: ${error}`, errors: [String(error)] }); } finally { setIsCompiling(false); } }; if (!isOpen) return null; const context = createContext(); return (

{compiler?.name || '编译器配置'}

{compiler?.createConfigUI ? ( compiler.createConfigUI(handleOptionsChange, context) ) : (
{compiler ? '该编译器没有配置界面' : '编译器未找到'}
)}
{compileResult && (
{compileResult.message}
{compileResult.outputFiles && compileResult.outputFiles.length > 0 && (
已生成 {compileResult.outputFiles.length} 个文件
)} {compileResult.errors && compileResult.errors.length > 0 && (
{compileResult.errors.map((err, i) => (
{err}
))}
)}
)}
); };