Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import re | |
| import json | |
| # 初始文件结构 | |
| initial_files = { | |
| "children": [ | |
| {"name": "index.html", "type": "file", "content": "<!DOCTYPE html>\n<!-- 注释示例 -->\n<html>\n<head>\n <title>示例</title>\n</head>\n<body>\n <h1 class=\"title\">Hello World</h1>\n</body>\n</html>"}, | |
| {"name": "styles", "type": "folder", "children": [ | |
| {"name": "main.css", "type": "file", "content": "/* CSS文件 */\nbody {\n background-color: #f0f0f0;\n}"} | |
| ]} | |
| ] | |
| } | |
| # HTML语法高亮函数 | |
| def highlight_html(html_str): | |
| # 处理注释 | |
| html_str = re.sub(r'(<!--.*?-->)', r'<span style="color:#888;font-style:italic">\1</span>', html_str) | |
| # 处理元素名 | |
| html_str = re.sub(r'(</?\w+)', r'<span style="color:#e06c75">\1</span>', html_str) | |
| # 处理属性名 | |
| html_str = re.sub(r'(\s\w+)=', r'<span style="color:#d19a66">\1</span>=', html_str) | |
| # 处理属性值 | |
| html_str = re.sub(r'=("[^"]*"|\'[^\']*\')', r'=<span style="color:#98c379">\1</span>', html_str) | |
| # 处理结束符 | |
| html_str = re.sub(r'(>)', r'<span style="color:#e06c75">\1</span>', html_str) | |
| return f'<pre style="background:#2c2c2c;color:#abb2bf;padding:20px;border-radius:5px">{html_str}</pre>' | |
| # 文件选择处理函数 | |
| def handle_file_select(evt: gr.SelectData): | |
| if evt.value["type"] == "file": | |
| content = evt.value["content"] | |
| if evt.value["name"].endswith(".html"): | |
| escaped = content.replace("<", "<").replace(">", ">") | |
| return highlight_html(escaped), content | |
| return gr.update(visible=False), content | |
| return gr.update(visible=False), "" | |
| # 创建新文件 | |
| def create_file(file_name, current_tree): | |
| if not file_name.strip(): | |
| return current_tree, "文件名不能为空!" | |
| new_file = { | |
| "name": file_name, | |
| "type": "file", | |
| "content": f"// {file_name} 的内容" | |
| } | |
| updated_tree = json.loads(current_tree) | |
| updated_tree["children"].append(new_file) | |
| return json.dumps(updated_tree), f"已创建文件: {file_name}" | |
| # 创建新文件夹 | |
| def create_folder(folder_name, current_tree): | |
| if not folder_name.strip(): | |
| return current_tree, "文件夹名不能为空!" | |
| new_folder = { | |
| "name": folder_name, | |
| "type": "folder", | |
| "children": [] | |
| } | |
| updated_tree = json.loads(current_tree) | |
| updated_tree["children"].append(new_folder) | |
| return json.dumps(updated_tree), f"已创建文件夹: {folder_name}" | |
| # 保存文件内容 | |
| def save_file_content(file_tree, file_content, selected_file): | |
| if not selected_file: | |
| return "未选择文件!", file_tree | |
| tree_data = json.loads(file_tree) | |
| def update_content(node): | |
| if node.get("name") == selected_file: | |
| node["content"] = file_content | |
| return True | |
| if "children" in node: | |
| for child in node["children"]: | |
| if update_content(child): | |
| return True | |
| return False | |
| if update_content(tree_data): | |
| return "文件已保存!", json.dumps(tree_data) | |
| return "保存失败!", file_tree | |
| # 创建文件树组件 | |
| def create_file_tree(): | |
| return gr.Tree( | |
| value=initial_files, | |
| label="文件浏览器", | |
| show_label=True, | |
| elem_classes=["file-tree"], | |
| ) | |
| # Gradio界面 | |
| with gr.Blocks(theme=gr.themes.Soft(), css=".file-tree {height: 500px;}") as demo: | |
| gr.Markdown("# 🧩 网页开发IDE") | |
| file_tree = gr.State(json.dumps(initial_files)) | |
| selected_file = gr.State("") | |
| with gr.Row(): | |
| # 左侧文件操作面板 | |
| with gr.Column(scale=1, min_width=250): | |
| create_file_btn = gr.Button("📄 新建文件", variant="primary") | |
| new_file_name = gr.Textbox(placeholder="输入文件名 (如: script.js)") | |
| create_folder_btn = gr.Button("📁 新建文件夹") | |
| new_folder_name = gr.Textbox(placeholder="输入文件夹名") | |
| status = gr.Textbox(label="操作状态", interactive=False) | |
| tree = create_file_tree() | |
| # 右侧代码编辑区 | |
| with gr.Column(scale=3): | |
| file_name_display = gr.Textbox(label="当前文件", interactive=False) | |
| html_preview = gr.HTML(visible=False) | |
| code_editor = gr.Code( | |
| language="html", | |
| label="代码编辑器", | |
| lines=20, | |
| interactive=True | |
| ) | |
| save_btn = gr.Button("💾 保存文件", variant="primary") | |
| save_status = gr.Textbox(interactive=False) | |
| # 事件处理 | |
| tree.select(handle_file_select, outputs=[html_preview, code_editor]) | |
| tree.select(lambda evt: evt.value["name"] if evt.value["type"]=="file" else "", outputs=[file_name_display]) | |
| tree.select(lambda evt: evt.value["name"] if evt.value["type"]=="file" else "", outputs=[selected_file]) | |
| create_file_btn.click( | |
| create_file, | |
| [new_file_name, file_tree], | |
| [file_tree, status] | |
| ).then(lambda: gr.Tree(value=json.loads(file_tree.value)), tree) | |
| create_folder_btn.click( | |
| create_folder, | |
| [new_folder_name, file_tree], | |
| [file_tree, status] | |
| ).then(lambda: gr.Tree(value=json.loads(file_tree.value)), tree) | |
| save_btn.click( | |
| save_file_content, | |
| [file_tree, code_editor, selected_file], | |
| [save_status, file_tree] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() |