Spaces:
Configuration error
Configuration error
| import re | |
| from pathlib import Path | |
| from typing import Dict, Optional, Tuple | |
| from dataclasses import dataclass | |
| from enum import Enum | |
| class Language(Enum): | |
| PYTHON = "python" | |
| JAVASCRIPT = "javascript" | |
| HTML = "html" | |
| CSS = "css" | |
| JSON = "json" | |
| MARKDOWN = "markdown" | |
| PLAINTEXT = "plaintext" | |
| JAVA = "java" | |
| CPP = "cpp" | |
| TYPESCRIPT = "typescript" | |
| SQL = "sql" | |
| class CodeFile: | |
| name: str | |
| content: str | |
| language: Language | |
| logo: str | |
| extension: str | |
| can_preview: bool = False | |
| class FileManager: | |
| """Manages code files, language detection, and logos""" | |
| # Language detection mapping | |
| LANGUAGE_MAP = { | |
| # Extensions to Language | |
| ".py": Language.PYTHON, | |
| ".js": Language.JAVASCRIPT, | |
| ".html": Language.HTML, | |
| ".htm": Language.HTML, | |
| ".css": Language.CSS, | |
| ".json": Language.JSON, | |
| ".md": Language.MARKDOWN, | |
| ".java": Language.JAVA, | |
| ".cpp": Language.CPP, | |
| ".c": Language.CPP, | |
| ".ts": Language.TYPESCRIPT, | |
| ".sql": Language.SQL, | |
| # Shebang detection for Python | |
| "#!/usr/bin/env python": Language.PYTHON, | |
| "#!/usr/bin/python": Language.PYTHON, | |
| } | |
| # Logo mappings (emoji fallbacks, can be replaced with image URLs) | |
| LOGO_MAP = { | |
| Language.PYTHON: "🐍", # Or use: "https://cdn.jsdelivr.net/npm/simple-icons@v13/icons/python.svg" | |
| Language.JAVASCRIPT: "🟨", | |
| Language.HTML: "🌐", | |
| Language.CSS: "🎨", | |
| Language.JSON: "📋", | |
| Language.MARKDOWN: "📝", | |
| Language.JAVA: "☕", | |
| Language.CPP: "⚙️", | |
| Language.TYPESCRIPT: "📘", | |
| Language.SQL: "🗄️", | |
| Language.PLAINTEXT: "📄", | |
| } | |
| # Previewable languages | |
| PREVIEWABLE = {Language.HTML, Language.JAVASCRIPT, Language.CSS} | |
| def detect_language(cls, filename: str, content: str = "") -> Language: | |
| """Detect language from filename and content""" | |
| # Check by extension | |
| ext = Path(filename).suffix.lower() | |
| if ext in cls.LANGUAGE_MAP: | |
| return cls.LANGUAGE_MAP[ext] | |
| # Check for shebang in first line | |
| if content: | |
| first_line = content.split('\n')[0].strip() | |
| if first_line in cls.LANGUAGE_MAP: | |
| return cls.LANGUAGE_MAP[first_line] | |
| # Heuristic detection | |
| if content: | |
| if "<html" in content.lower() or "<body" in content.lower(): | |
| return Language.HTML | |
| if "def " in content and "import " in content: | |
| return Language.PYTHON | |
| if "function " in content or "const " in content: | |
| return Language.JAVASCRIPT | |
| return Language.PLAINTEXT | |
| def get_logo(cls, language: Language, use_emoji: bool = True) -> str: | |
| """Get logo for language (emoji or HTML img tag)""" | |
| if use_emoji: | |
| return cls.LOGO_MAP.get(language, "📄") | |
| # For image URLs, you can use: | |
| # return f'<img src="https://cdn.jsdelivr.net/npm/simple-icons@v13/icons/{language.value}.svg" width="16" height="16">' | |
| return cls.LOGO_MAP.get(language, "📄") | |
| def extract_files_from_code(cls, code: str) -> Dict[str, CodeFile]: | |
| """Extract multiple files from code block (e.g., from AI response)""" | |
| files = {} | |
| # Pattern for markdown code blocks with filenames | |
| # Matches: ```python filename.py\ncode``` | |
| pattern = r'```(\w+)(?:\s+([\/\w\.-]+))?\n(.*?)```' | |
| matches = re.findall(pattern, code, re.DOTALL) | |
| for lang, filename, content in matches: | |
| if not filename: | |
| # Generate filename from language | |
| ext_map = { | |
| "python": "script.py", | |
| "javascript": "script.js", | |
| "html": "index.html", | |
| "css": "styles.css", | |
| "json": "data.json", | |
| "java": "Main.java", | |
| "cpp": "program.cpp", | |
| } | |
| filename = ext_map.get(lang.lower(), f"code.{lang}") | |
| language = cls.detect_language(filename, content) | |
| files[filename] = CodeFile( | |
| name=filename, | |
| content=content.strip(), | |
| language=language, | |
| logo=cls.get_logo(language), | |
| extension=Path(filename).suffix, | |
| can_preview=language in cls.PREVIEWABLE | |
| ) | |
| return files | |
| def format_file_tree(cls, files: Dict[str, CodeFile]) -> str: | |
| """Generate HTML for file tree with logos""" | |
| html = '<div class="file-tree">' | |
| for name, file in files.items(): | |
| html += f''' | |
| <div class="file-item" data-filename="{name}" data-language="{file.language.value}"> | |
| <span class="file-logo">{file.logo}</span> | |
| <span class="file-name">{name}</span> | |
| <span class="file-badge">{file.language.value}</span> | |
| </div> | |
| ''' | |
| html += '</div>' | |
| return html | |
| # Test | |
| if __name__ == "__main__": | |
| test_code = """ | |
| Here's a Python function: | |
| ```python | |
| def hello(): | |
| print("Hello World") | |
| And an HTML file: | |
| html | |
| <h1>Hello</h1> | |
| """ | |
| files = FileManager.extract_files_from_code(test_code) | |
| for name, file in files.items(): | |
| print(f"{file.logo} {name} ({file.language.value})") | |
| text | |
| ## 📄 File 3: `preview_handler.py` | |
| ```python | |
| import base64 | |
| import re | |
| from typing import Dict, Optional | |
| from file_manager import CodeFile, Language | |
| class PreviewHandler: | |
| """Handles live previews for different code types""" | |
| def create_html_preview(content: str) -> str: | |
| """Create an iframe-ready HTML preview""" | |
| # Sanitize content (basic) | |
| # In production, use a proper sanitizer like bleach | |
| return f""" | |
| <div class="preview-container"> | |
| <iframe srcdoc="{content.replace('"', '"')}" | |
| style="width:100%; height:500px; border:none; border-radius:8px;" | |
| sandbox="allow-same-origin allow-scripts allow-popups allow-forms"> | |
| </iframe> | |
| </div> | |
| """ | |
| def create_js_preview(content: str) -> str: | |
| """Run JavaScript code in a sandboxed environment""" | |
| # Wrap JS in HTML for preview | |
| html = f""" | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>JavaScript Preview</title> | |
| <style> | |
| body {{ font-family: monospace; padding: 20px; background: #1e1e1e; color: #d4d4d4; }} | |
| #output {{ background: #2d2d2d; padding: 10px; border-radius: 5px; margin-top: 10px; }} | |
| </style> | |
| </head> | |
| <body> | |
| <div id="output">Console output will appear here...</div> | |
| <script> | |
| // Capture console.log | |
| (function() {{ | |
| const output = document.getElementById('output'); | |
| const originalLog = console.log; | |
| console.log = function(...args) {{ | |
| output.innerHTML += args.map(a => String(a)).join(' ') + '<br>'; | |
| originalLog.apply(console, args); | |
| }}; | |
| }})(); | |
| // User code | |
| try {{ | |
| {content} | |
| }} catch(e) {{ | |
| console.log('Error:', e.message); | |
| }} | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| return PreviewHandler.create_html_preview(html) | |
| def create_python_preview(content: str) -> str: | |
| """ | |
| Create a Python preview using PyScript (runs in browser) | |
| Note: Limited functionality - no file I/O, no network requests | |
| """ | |
| # Encode content for safe embedding | |
| encoded_content = base64.b64encode(content.encode()).decode() | |
| return f""" | |
| <div class="preview-container"> | |
| <link rel="stylesheet" href="https://pyscript.net/latest/pyscript.css" /> | |
| <script defer src="https://pyscript.net/latest/pyscript.js"></script> | |
| <py-script> | |
| import base64 | |
| # Decode and execute user code | |
| code = base64.b64decode("{encoded_content}").decode() | |
| exec(code) | |
| </py-script> | |
| <div class="pyscript-output" style="background:#1e1e1e; padding:10px; border-radius:5px;"> | |
| Python output will appear here... | |
| </div> | |
| </div> | |
| """ | |
| def create_preview(file: CodeFile) -> str: | |
| """Generate appropriate preview based on file language""" | |
| if not file.can_preview: | |
| return f""" | |
| <div class="preview-error"> | |
| ⚠️ Preview not available for {file.language.value.upper()} files. | |
| <br>Supported preview formats: HTML, CSS, JavaScript | |
| </div> | |
| """ | |
| if file.language == Language.HTML: | |
| return PreviewHandler.create_html_preview(file.content) | |
| elif file.language == Language.JAVASCRIPT: | |
| return PreviewHandler.create_js_preview(file.content) | |
| elif file.language == Language.CSS: | |
| # Wrap CSS in minimal HTML | |
| html = f""" | |
| <!DOCTYPE html> | |
| <html> | |
| <head><style>{file.content}</style></head> | |
| <body> | |
| <div class="preview-demo"> | |
| <h1>CSS Preview</h1> | |
| <p>Your styles are applied to this page.</p> | |
| <button>Test Button</button> | |
| </div> | |
| </body> | |
| </html> | |
| """ | |
| return PreviewHandler.create_html_preview(html) | |
| elif file.language == Language.PYTHON: | |
| return PreviewHandler.create_python_preview(file.content) | |
| return "<div>Preview not available</div>" | |
| def format_code_with_syntax(content: str, language: str) -> str: | |
| """Return formatted code HTML (for the 'View Code' tab)""" | |
| # Escape HTML | |
| escaped = content.replace("&", "&").replace("<", "<").replace(">", ">") | |
| return f""" | |
| <div class="code-viewer"> | |
| <div class="code-header"> | |
| <span class="code-lang">{language.upper()}</span> | |
| <button class="copy-btn" onclick="navigator.clipboard.writeText(`{escaped.replace('`', '\\`')}`)"> | |
| 📋 Copy | |
| </button> | |
| </div> | |
| <pre class="code-block"><code class="language-{language}">{escaped}</code></pre> | |
| </div> | |
| """ |