Spaces:
Runtime error
Runtime error
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() |