import { BlockDefinition, ProjectFile, VisualElement } from '../types/blocks'; interface CompileOptions { blocks: BlockDefinition[]; fileTree: ProjectFile[]; visualElements: VisualElement[]; activeFile?: ProjectFile; projectName: string; } export function compileWeb(options: CompileOptions): string { const { fileTree, activeFile, blocks } = options; if (!activeFile) { // Generate default index.html with all visual elements return generateDefaultHTML(options); } switch (activeFile.type) { case 'html': return activeFile.content || generateDefaultHTML(options); case 'css': return activeFile.content || '/* Styles */\n'; case 'js': return activeFile.content || '// JavaScript\n'; default: return activeFile.content || ''; } } function findFileInTree(files: ProjectFile[], id: string): ProjectFile | null { for (const f of files) { if (f.id === id) return f; if (f.children) { const found = findFileInTree(f.children, id); if (found) return found; } } return null; } function generateDefaultHTML(options: CompileOptions): string { const { visualElements, projectName, fileTree, activeFile } = options; // Find linked CSS and JS files const linkedIds = activeFile?.linkedFiles || []; const linkedFiles = linkedIds.map(id => findFileInTree(fileTree, id)).filter(Boolean) as ProjectFile[]; const linkedCSS = linkedFiles.filter(f => f.type === 'css'); const linkedJS = linkedFiles.filter(f => f.type === 'js'); // Build link and script tags const cssLinks = linkedCSS.map(f => ` `).join('\n'); const jsScripts = linkedJS.map(f => ` `).join('\n'); return ` ${projectName || 'My Web App'} ${cssLinks} ${visualElements.map(el => renderVisualElement(el, 0)).join('\n')} ${jsScripts} `; } export function renderVisualElement(el: VisualElement, depth: number): string { const indent = ' '.repeat(depth + 1); const props = Object.entries(el.props) .filter(([k, v]) => v !== undefined && v !== null && v !== '' && k !== 'textContent' && k !== 'children') .map(([k, v]) => { if (typeof v === 'boolean') return v ? k : ''; if (k === 'style') return ''; return `${k}="${String(v).replace(/"/g, '"')}"`; }) .filter(Boolean) .join(' '); const styles = Object.entries(el.styles) .map(([k, v]) => `${k.replace(/[A-Z]/g, m => '-' + m.toLowerCase())}: ${v}`) .join('; '); const styleAttr = styles ? ` style="${styles}"` : ''; const classAttr = el.classes.length ? ` class="${el.classes.join(' ')}"` : ''; const propsStr = props ? ' ' + props : ''; const attrs = propsStr + styleAttr + classAttr; const content = el.props.textContent || ''; if (el.children && el.children.length > 0) { return `${indent}<${el.tagName}${attrs}> ${el.children.map(c => renderVisualElement(c, depth + 1)).join('\n')} ${indent}`; } if (el.tagName === 'img' || el.tagName === 'input' || el.tagName === 'br') { return `${indent}<${el.tagName}${attrs} />`; } return `${indent}<${el.tagName}${attrs}>${content || ''}`; } export function compileElectron(options: CompileOptions): string { const { activeFile } = options; if (!activeFile) return '// Electron main process\n'; switch (activeFile.type) { case 'typescript': return activeFile.content || generateDefaultElectronMain(options); case 'html': return activeFile.content || generateDefaultHTML(options); case 'css': return activeFile.content || '/* Styles */\n'; default: return activeFile.content || ''; } } function generateDefaultElectronMain(options: CompileOptions): string { return `import { app, BrowserWindow, ipcMain } from 'electron'; import path from 'path'; let mainWindow: BrowserWindow | null = null; function createWindow(): void { mainWindow = new BrowserWindow({ width: 1200, height: 800, webPreferences: { preload: path.join(__dirname, 'preload.js'), nodeIntegration: false, contextIsolation: true, }, }); mainWindow.loadFile('index.html'); mainWindow.on('closed', () => { mainWindow = null; }); } app.whenReady().then(() => { createWindow(); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); `; } export function compileMaui(options: CompileOptions): string { const { activeFile } = options; if (!activeFile) return '\n'; switch (activeFile.type) { case 'xaml': return activeFile.content || generateDefaultXaml(options); case 'csharp': return activeFile.content || generateDefaultCSharp(options); default: return activeFile.content || ''; } } function generateDefaultXaml(options: CompileOptions): string { return `