import os import json import zipfile import shutil import subprocess import tempfile from pathlib import Path from typing import Dict, List, Optional import threading import time from http.server import HTTPServer, SimpleHTTPRequestHandler import socket class FileHandler: """Handle file operations for website projects""" def __init__(self): self.projects_dir = Path("projects") self.projects_dir.mkdir(exist_ok=True) def create_project(self, project_name: str, files: Dict[str, str]) -> Path: """Create a new project with generated files""" project_path = self.projects_dir / project_name # Remove existing project if it exists if project_path.exists(): shutil.rmtree(project_path) project_path.mkdir(exist_ok=True) # Write all files for file_path, content in files.items(): full_path = project_path / file_path full_path.parent.mkdir(parents=True, exist_ok=True) with open(full_path, 'w', encoding='utf-8') as f: f.write(content) return project_path def get_project_files(self, project_path: Path) -> Dict[str, str]: """Get all files in a project""" files = {} if project_path.exists(): for file_path in project_path.rglob("*"): if file_path.is_file(): relative_path = file_path.relative_to(project_path) files[str(relative_path)] = self.get_file_content(project_path, str(relative_path)) return files def get_file_content(self, project_path: Path, filename: str) -> str: """Get content of a specific file""" file_path = project_path / filename if file_path.exists(): with open(file_path, 'r', encoding='utf-8') as f: return f.read() return "" def create_zip(self, project_path: Path) -> str: """Create a ZIP file of the project""" zip_path = f"{project_path}.zip" with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: for file_path in project_path.rglob("*"): if file_path.is_file(): arcname = file_path.relative_to(project_path.parent) zipf.write(file_path, arcname) return zip_path class PreviewManager: """Manage live preview of websites""" def __init__(self): self.active_servers = {} self.port_counter = 8000 def get_free_port(self) -> int: """Find a free port for the preview server""" while True: try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.bind(('localhost', self.port_counter)) return self.port_counter except OSError: self.port_counter += 1 def create_preview(self, project_path: Path) -> str: """Create a live preview server for the project""" port = self.get_free_port() # Start server in a separate thread server_thread = threading.Thread( target=self._run_server, args=(project_path, port), daemon=True ) server_thread.start() # Give server time to start time.sleep(0.5) self.active_servers[str(project_path)] = port return f"http://localhost:{port}" def update_preview(self, project_path: Path) -> str: """Update the preview (server already running)""" if str(project_path) in self.active_servers: port = self.active_servers[str(project_path)] return f"http://localhost:{port}" return self.create_preview(project_path) def _run_server(self, project_path: Path, port: int): """Run HTTP server for preview""" os.chdir(project_path) class CustomHandler(SimpleHTTPRequestHandler): def __init__(self, *args, **kwargs): super().__init__(*args, directory=project_path, **kwargs) def end_headers(self): # Add CORS headers self.send_header('Access-Control-Allow-Origin', '*') self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') self.send_header('Access-Control-Allow-Headers', 'Content-Type') super().end_headers() try: httpd = HTTPServer(('localhost', port), CustomHandler) httpd.serve_forever() except Exception as e: print(f"Server error: {e}") def detect_file_language(filename: str) -> str: """Detect programming language from file extension""" ext = Path(filename).suffix.lower() lang_map = { '.html': 'html', '.css': 'css', '.js': 'javascript', '.json': 'json', '.md': 'markdown', '.py': 'python', '.jsx': 'jsx', '.tsx': 'typescript', '.ts': 'typescript', '.xml': 'xml', '.yaml': 'yaml', '.yml': 'yaml', '.sql': 'sql', '.php': 'php', '.rb': 'ruby', '.go': 'go', '.java': 'java', '.c': 'c', '.cpp': 'cpp', '.h': 'c', '.hpp': 'cpp', '.scss': 'scss', '.sass': 'sass', '.less': 'less', '.vue': 'vue', '.svelte': 'svelte' } return lang_map.get(ext, 'text') def format_file_size(size_bytes: int) -> str: """Format file size in human readable format""" if size_bytes == 0: return "0B" size_names = ["B", "KB", "MB", "GB"] i = 0 while size_bytes >= 1024 and i < len(size_names) - 1: size_bytes /= 1024.0 i += 1 return f"{size_bytes:.1f}{size_names[i]}"