gradio-IDE / app.py
akhaliq's picture
akhaliq HF staff
Update app.py
1b97373 verified
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()