import gradio as gr import time import os from pathlib import Path # Simulated file storage (in-memory dictionary) files = {"untitled.py": ""} # file_name: content open_files = ["untitled.py"] # List of open files # Load file content into editor def load_file(file, current_file, open_files): global files if file is not None: with open(file.name, "r") as f: code = f.read() filename = file.name.split("/")[-1] files[filename] = code if filename not in open_files: open_files.append(filename) return code, filename, open_files, code return files.get(current_file, ""), current_file, open_files, files.get(current_file, "") # Load file from FileExplorer def load_from_explorer(selected_files, files, open_files): if isinstance(selected_files, str): # Single file selected selected_files = [selected_files] if selected_files and selected_files[0]: filename = os.path.basename(selected_files[0]) with open(selected_files[0], "r") as f: code = f.read() files[filename] = code if filename not in open_files: open_files.append(filename) return code, filename, open_files, code return "", list(files.keys())[0], open_files, "" # Save current file content def save_file(code, current_file): global files files[current_file] = code return f"Saved {current_file}" # Simulate running code def run_code(code, terminal_history): timestamp = time.strftime("%H:%M:%S") output = f"[{timestamp}] Ran:\n{code}\nOutput: Simulated execution successful" return f"{terminal_history}\n{output}".strip() # Process terminal commands def process_command(cmd, files, history): if cmd.strip() == "ls": output = "\n".join(files.keys()) elif cmd.startswith("echo "): output = cmd[5:] else: output = f"Command not recognized: {cmd}" return f"{history}\n$ {cmd}\n{output}" # AI-powered suggestions def get_suggestions(code): code = code.lower() if "def" in code: return ["def function_name():", "def main():", "def __init__(self):"] elif "print" in code: return ["print('Hello, World!')", "print(variable)", "print(f'String: {var}')"] elif "import" in code: return ["import os", "import sys", "import numpy as np"] return ["# Start typing for suggestions"] # Insert suggestion def insert_suggestion(code, suggestion): if suggestion: lines = code.split("\n") last_line = lines[-1] return code + ("\n" + suggestion if last_line.strip() else suggestion) return code # Switch tabs def switch_tab(selected, files): return files.get(selected, ""), selected # Add new file def add_new_file(open_files, files): new_file = f"untitled_{len(files)}.py" files[new_file] = "" open_files.append(new_file) return "", new_file, open_files, files[new_file] # Close tab def close_tab(open_files, current_file, files): if len(open_files) > 1: new_open_files = [f for f in open_files if f != current_file] new_current = new_open_files[0] return new_open_files, new_current, files[new_current] return open_files, current_file, files[current_file] # Download file def download_code(code, current_file): return code, current_file # Process command palette def process_palette(cmd, theme, files, current_file, code): if cmd == "theme dark": return "dark", "Theme set to dark" elif cmd == "theme light": return "light", "Theme set to light" elif cmd == "save": files[current_file] = code return theme, f"Saved {current_file}" return theme, f"Command not found: {cmd}" # Gradio interface with gr.Blocks( title="VS Code Clone", css=""" .gradio-container { background-color: #1e1e1e; color: #d4d4d4; font-family: 'Consolas', monospace; height: 100vh; margin: 0; } .activity-bar { background-color: #333; padding: 10px; width: 50px; min-width: 50px; } .sidebar { background-color: #252526; padding: 10px; width: 250px; min-width: 250px; } .editor { background-color: #1e1e1e; border: none; } .terminal { background-color: #0e0e0e; color: #b5cea8; font-size: 12px; } .status-bar { background-color: #007acc; color: white; padding: 5px; } .tabs { display: flex; gap: 5px; background-color: #1e1e1e; padding: 5px; border-bottom: 1px solid #3c3c3c; } .tab { background-color: #2d2d2d; padding: 5px 10px; cursor: pointer; } .tab.active { background-color: #007acc; } button { background-color: #007acc; color: white; border: none; padding: 5px; margin: 2px; } button:hover { background-color: #005f99; } .file-explorer { max-height: 300px; overflow-y: auto; } .command-palette { position: absolute; top: 20%; left: 30%; width: 40%; background: #252526; z-index: 1000; padding: 10px; } .light .gradio-container { background-color: #ffffff; color: #000000; } .light .editor { background-color: #ffffff; color: #000000; } .light .sidebar { background-color: #f3f3f3; } .light .terminal { background-color: #f0f0f0; color: #000000; } .light .status-bar { background-color: #0066cc; } .light .tabs { background-color: #ffffff; } .light .tab { background-color: #e0e0e0; } .light .tab.active { background-color: #0066cc; } """ ) as app: # States current_file = gr.State(value="untitled.py") terminal_history = gr.State(value="") theme_state = gr.State(value="dark") cursor_position = gr.State(value="Ln 1, Col 1") # Layout with gr.Row(): # Activity Bar with gr.Column(scale=0, elem_classes="activity-bar"): explorer_btn = gr.Button("📁") terminal_btn = gr.Button("🖥️") # Sidebar (File Explorer) with gr.Column(scale=1, visible=False, elem_classes="sidebar") as sidebar: gr.Markdown("### Explorer") file_upload = gr.File(label="Upload File") file_explorer = gr.FileExplorer( label="Files", glob="*.py", file_count="single", root_dir=os.getcwd(), elem_classes="file-explorer" ) # Editor Pane with gr.Column(scale=4): with gr.Row(elem_classes="tabs"): file_tabs = gr.Dropdown(label="Open Files", choices=open_files, value="untitled.py", interactive=True) new_file_btn = gr.Button("➕") close_tab_btn = gr.Button("❌") code_editor = gr.Code(label="Editor", language="python", lines=20, elem_classes="editor") with gr.Row(): with gr.Column(scale=3): terminal_panel = gr.Column(visible=False, elem_classes="terminal") with terminal_panel: terminal_output = gr.Textbox(label="Terminal", lines=10, interactive=False) terminal_input = gr.Textbox(label=">", placeholder="Type command and press Enter") with gr.Column(scale=1): suggestions_list = gr.Radio(choices=[], label="Suggestions", type="value") # Status Bar with gr.Row(elem_classes="status-bar"): run_btn = gr.Button("▶ Run") save_btn = gr.Button("💾 Save") status_info = gr.Textbox(value="Python • Ln 1, Col 1", interactive=False) # Command Palette command_palette = gr.Textbox(label="> Command Palette", visible=False, elem_classes="command-palette") # Hidden Download Output download_output = gr.File(label="Download", visible=False) # JavaScript for shortcuts and cursor tracking app.load(js=""" () => { const editor = document.querySelector('#code_editor textarea'); const status = document.querySelector('#status_info input'); const palette = document.querySelector('#command_palette'); const themeInput = document.querySelector('#theme_state input'); const container = document.querySelector('.gradio-container'); // Cursor position function getCursorPosition(textarea) { const text = textarea.value; const pos = textarea.selectionStart; const lines = text.substr(0, pos).split('\\n'); return `Python • Ln ${lines.length}, Col ${lines[lines.length - 1].length + 1}`; } editor.addEventListener('keyup', () => { status.value = getCursorPosition(editor); status.dispatchEvent(new Event('input')); }); editor.addEventListener('click', () => { status.value = getCursorPosition(editor); status.dispatchEvent(new Event('input')); }); // Shortcuts document.addEventListener('keydown', (e) => { if (e.ctrlKey && e.shiftKey && e.key === 'P') { e.preventDefault(); palette.style.display = 'block'; palette.querySelector('input').focus(); } if (e.ctrlKey && e.key === 's') { e.preventDefault(); document.querySelector('#save_btn').click(); } if (e.ctrlKey && e.key === 'r') { e.preventDefault(); document.querySelector('#run_btn').click(); } }); // Hide palette on Escape palette.querySelector('input').addEventListener('keydown', (e) => { if (e.key === 'Escape') { palette.style.display = 'none'; } }); // Theme switching container.classList.add(themeInput.value); const observer = new MutationObserver(() => { container.classList.remove('light', 'dark'); container.classList.add(themeInput.value); }); observer.observe(themeInput, {attributes: true}); } """, outputs=[status_info]) # Event Handlers explorer_btn.click(lambda v: gr.update(visible=not v), inputs=[sidebar.visible], outputs=[sidebar]) terminal_btn.click(lambda v: gr.update(visible=not v), inputs=[terminal_panel.visible], outputs=[terminal_panel]) file_upload.change( load_file, inputs=[file_upload, current_file, file_tabs.choices], outputs=[code_editor, current_file, file_tabs, code_editor] ) file_explorer.change( load_from_explorer, inputs=[file_explorer, files, file_tabs.choices], outputs=[code_editor, current_file, file_tabs, code_editor] ) save_btn.click( save_file, inputs=[code_editor, current_file], outputs=[status_info] ).then( download_code, inputs=[code_editor, current_file], outputs=[download_output] ) run_btn.click( run_code, inputs=[code_editor, terminal_history], outputs=[terminal_output] ).then( lambda x: x, inputs=[terminal_output], outputs=[terminal_history] ) terminal_input.submit( process_command, inputs=[terminal_input, files, terminal_history], outputs=[terminal_output] ).then(lambda: "", outputs=[terminal_input]).then( lambda x: x, inputs=[terminal_output], outputs=[terminal_history] ) code_editor.change( get_suggestions, inputs=[code_editor], outputs=[suggestions_list] ) suggestions_list.change( insert_suggestion, inputs=[code_editor, suggestions_list], outputs=[code_editor] ) file_tabs.change( switch_tab, inputs=[file_tabs, files], outputs=[code_editor, current_file] ) new_file_btn.click( add_new_file, inputs=[file_tabs.choices, files], outputs=[code_editor, current_file, file_list, code_editor] ) close_tab_btn.click( close_tab, inputs=[file_tabs.choices, current_file, files], outputs=[file_tabs.choices, current_file, code_editor] ).then( lambda open_files, current_file: gr.update(choices=open_files, value=current_file), inputs=[file_tabs.choices, current_file], outputs=[file_tabs] ) command_palette.submit( process_palette, inputs=[command_palette, theme_state, files, current_file, code_editor], outputs=[theme_state, status_info] ).then(lambda: gr.update(visible=False), outputs=[command_palette]) # Launch app.launch()