pangxiang's picture
Update app.py
5207fa8 verified
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
CodeGPS Pro - 完全实用版
真正能用的大模型代码眼睛 - 所有功能都能独立工作
"""
import gradio as gr
import re
import difflib
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
import json
# ==================== 核心引擎 ====================
@dataclass
class CodeBlock:
start_line: int
end_line: int
type: str
name: str
content: str
parent: Optional[str] = None
class CodeGPSEngine:
"""核心代码处理引擎"""
def __init__(self):
self.reset()
def reset(self):
"""重置状态"""
self.original_code = ""
self.current_code = ""
self.lines = []
self.language = "unknown"
self.blocks = []
self.history = []
self.is_loaded = False
def auto_load(self, code: str) -> str:
"""自动加载代码(无需手动触发)"""
if not code or not code.strip():
self.reset()
return "⚠️ 代码为空"
self.original_code = code
self.current_code = code
self.lines = code.split('\n')
self.language = self._detect_language(code)
self.blocks = self._parse_blocks()
self.is_loaded = True
return f"✅ 已加载 | 语言:{self.language.upper()} | 行数:{len(self.lines)} | 代码块:{len(self.blocks)}"
def _detect_language(self, code: str) -> str:
"""快速检测语言"""
patterns = {
'python': [r'def\s+\w+', r'class\s+\w+', r'import\s+\w+', r':\s*$'],
'javascript': [r'function\s+', r'const\s+\w+\s*=', r'=>', r'console\.'],
'java': [r'public\s+class', r'private\s+\w+', r'System\.'],
'cpp': [r'#include', r'std::', r'int\s+main'],
'go': [r'package\s+', r'func\s+', r':='],
'php': [r'<\?php', r'\$\w+', r'function\s+\w+'],
'ruby': [r'def\s+\w+', r'end\s*$', r'require\s+'],
'rust': [r'fn\s+\w+', r'let\s+\w+', r'impl\s+'],
}
for lang, pats in patterns.items():
matches = sum(1 for p in pats if re.search(p, code, re.MULTILINE))
if matches >= 2:
return lang
return 'plaintext'
def _parse_blocks(self) -> List[CodeBlock]:
"""解析代码块"""
blocks = []
if self.language == 'python':
blocks = self._parse_python()
elif self.language in ['javascript', 'java', 'cpp', 'go']:
blocks = self._parse_braces()
return blocks
def _parse_python(self) -> List[CodeBlock]:
"""解析Python代码"""
blocks = []
i = 0
current_class = None
while i < len(self.lines):
line = self.lines[i]
stripped = line.lstrip()
indent = len(line) - len(stripped)
# 类定义
if re.match(r'class\s+(\w+)', stripped):
name = re.match(r'class\s+(\w+)', stripped).group(1)
start = i + 1
end = self._find_python_block_end(i, indent)
blocks.append(CodeBlock(
start_line=start,
end_line=end,
type='class',
name=name,
content='\n'.join(self.lines[i:end])
))
current_class = name
i = end
continue
# 函数/方法定义
if re.match(r'def\s+(\w+)', stripped):
name = re.match(r'def\s+(\w+)', stripped).group(1)
start = i + 1
end = self._find_python_block_end(i, indent)
blocks.append(CodeBlock(
start_line=start,
end_line=end,
type='method' if current_class else 'function',
name=name,
content='\n'.join(self.lines[i:end]),
parent=current_class
))
i = end
continue
i += 1
return blocks
def _find_python_block_end(self, start: int, base_indent: int) -> int:
"""查找Python代码块结束位置"""
i = start + 1
while i < len(self.lines):
line = self.lines[i]
if line.strip():
indent = len(line) - len(line.lstrip())
if indent <= base_indent:
return i
i += 1
return len(self.lines)
def _parse_braces(self) -> List[CodeBlock]:
"""解析花括号语言"""
blocks = []
stack = []
patterns = {
'function': r'(?:function\s+(\w+)|(\w+)\s*\([^)]*\)\s*\{|fn\s+(\w+)|func\s+(\w+))',
'class': r'class\s+(\w+)',
}
for i, line in enumerate(self.lines, 1):
# 检测函数
func_match = re.search(patterns['function'], line)
if func_match:
name = next((g for g in func_match.groups() if g), 'anonymous')
stack.append(('function', name, i, 0))
# 检测类
class_match = re.search(patterns['class'], line)
if class_match:
name = class_match.group(1)
stack.append(('class', name, i, 0))
# 花括号计数
open_count = line.count('{')
close_count = line.count('}')
if stack:
stack[-1] = (stack[-1][0], stack[-1][1], stack[-1][2], stack[-1][3] + open_count - close_count)
if stack[-1][3] == 0 and (open_count > 0 or close_count > 0):
block_type, name, start, _ = stack.pop()
blocks.append(CodeBlock(
start_line=start,
end_line=i,
type=block_type,
name=name,
content='\n'.join(self.lines[start-1:i])
))
return blocks
# ==================== 查询功能 ====================
def get_line(self, line_num: int) -> Dict:
"""获取单行"""
if not self.is_loaded:
return {'success': False, 'error': '❌ 请先输入代码'}
if not 1 <= line_num <= len(self.lines):
return {'success': False, 'error': f'❌ 行号超出范围 (1-{len(self.lines)})'}
return {
'success': True,
'content': self.lines[line_num - 1],
'formatted': f"第{line_num}行: {self.lines[line_num - 1]}"
}
def get_lines(self, start: int, end: int) -> Dict:
"""获取多行"""
if not self.is_loaded:
return {'success': False, 'error': '❌ 请先输入代码'}
total = len(self.lines)
if not (1 <= start <= total and 1 <= end <= total):
return {'success': False, 'error': f'❌ 行号超出范围 (1-{total})'}
if start > end:
start, end = end, start
lines_output = []
for i in range(max(1, start - 2), min(total + 1, end + 3)):
marker = '>>> ' if start <= i <= end else ' '
lines_output.append(f"{marker}{i:4d} | {self.lines[i-1]}")
return {
'success': True,
'content': '\n'.join(self.lines[start-1:end]),
'formatted': '\n'.join(lines_output)
}
def get_function(self, name: str) -> Dict:
"""获取函数"""
if not self.is_loaded:
return {'success': False, 'error': '❌ 请先输入代码'}
for block in self.blocks:
if block.type in ['function', 'method'] and block.name == name:
lines_output = []
for i in range(block.start_line, block.end_line + 1):
lines_output.append(f"{i:4d} | {self.lines[i-1]}")
return {
'success': True,
'content': block.content,
'formatted': f"📍 函数: {name} [第{block.start_line}-{block.end_line}行]\n" + '\n'.join(lines_output)
}
return {'success': False, 'error': f'❌ 未找到函数: {name}'}
def get_class(self, name: str) -> Dict:
"""获取类"""
if not self.is_loaded:
return {'success': False, 'error': '❌ 请先输入代码'}
for block in self.blocks:
if block.type == 'class' and block.name == name:
lines_output = []
for i in range(block.start_line, block.end_line + 1):
lines_output.append(f"{i:4d} | {self.lines[i-1]}")
return {
'success': True,
'content': block.content,
'formatted': f"📦 类: {name} [第{block.start_line}-{block.end_line}行]\n" + '\n'.join(lines_output)
}
return {'success': False, 'error': f'❌ 未找到类: {name}'}
def search_text(self, keyword: str) -> Dict:
"""搜索文本"""
if not self.is_loaded:
return {'success': False, 'error': '❌ 请先输入代码'}
results = []
for i, line in enumerate(self.lines, 1):
if keyword in line:
results.append(f"{i:4d} | {line}")
if results:
return {
'success': True,
'formatted': f"🔍 找到 {len(results)} 处匹配:\n" + '\n'.join(results[:50]) # 最多显示50个
}
return {'success': False, 'error': f'❌ 未找到: {keyword}'}
def list_all(self) -> str:
"""列出所有代码块"""
if not self.is_loaded:
return "❌ 请先输入代码"
output = [f"📋 代码结构 [{self.language.upper()}] 共{len(self.lines)}行\n"]
if not self.blocks:
output.append("⚠️ 未检测到明显的函数/类结构")
return '\n'.join(output)
# 按类型分组
classes = [b for b in self.blocks if b.type == 'class']
functions = [b for b in self.blocks if b.type == 'function']
methods = [b for b in self.blocks if b.type == 'method']
if classes:
output.append("📦 类定义:")
for b in classes:
output.append(f" ├─ {b.name} (第{b.start_line}-{b.end_line}行)")
if functions:
output.append("\n🔧 函数定义:")
for b in functions:
output.append(f" ├─ {b.name}() (第{b.start_line}-{b.end_line}行)")
if methods:
output.append("\n⚙️ 方法定义:")
for b in methods:
parent = f" [{b.parent}]" if b.parent else ""
output.append(f" ├─ {b.name}(){parent} (第{b.start_line}-{b.end_line}行)")
return '\n'.join(output)
# ==================== 修改功能 ====================
def replace_line(self, line_num: int, new_content: str) -> Dict:
"""替换单行"""
if not self.is_loaded:
return {'success': False, 'error': '❌ 请先输入代码'}
if not 1 <= line_num <= len(self.lines):
return {'success': False, 'error': f'❌ 行号超出范围 (1-{len(self.lines)})'}
old = self.lines[line_num - 1]
self.lines[line_num - 1] = new_content
self.current_code = '\n'.join(self.lines)
self._save_history('replace_line', line_num, old)
return {
'success': True,
'message': f'✅ 已替换第{line_num}行',
'diff': self._make_diff(old, new_content)
}
def replace_lines(self, start: int, end: int, new_content: str) -> Dict:
"""替换多行"""
if not self.is_loaded:
return {'success': False, 'error': '❌ 请先输入代码'}
total = len(self.lines)
if not (1 <= start <= total and 1 <= end <= total):
return {'success': False, 'error': f'❌ 行号超出范围 (1-{total})'}
if start > end:
start, end = end, start
old = '\n'.join(self.lines[start-1:end])
new_lines = new_content.split('\n')
self.lines[start-1:end] = new_lines
self.current_code = '\n'.join(self.lines)
self._save_history('replace_lines', (start, end), old)
return {
'success': True,
'message': f'✅ 已替换第{start}-{end}行 ({end-start+1}行 → {len(new_lines)}行)',
'diff': self._make_diff(old, new_content)
}
def insert_after(self, line_num: int, new_content: str) -> Dict:
"""在指定行后插入"""
if not self.is_loaded:
return {'success': False, 'error': '❌ 请先输入代码'}
if not 0 <= line_num <= len(self.lines):
return {'success': False, 'error': f'❌ 行号超出范围 (0-{len(self.lines)})'}
new_lines = new_content.split('\n')
self.lines[line_num:line_num] = new_lines
self.current_code = '\n'.join(self.lines)
self._save_history('insert', line_num, None)
return {
'success': True,
'message': f'✅ 已在第{line_num}行后插入{len(new_lines)}行'
}
def delete_lines(self, start: int, end: int) -> Dict:
"""删除行"""
if not self.is_loaded:
return {'success': False, 'error': '❌ 请先输入代码'}
total = len(self.lines)
if not (1 <= start <= total and 1 <= end <= total):
return {'success': False, 'error': f'❌ 行号超出范围 (1-{total})'}
if start > end:
start, end = end, start
deleted = '\n'.join(self.lines[start-1:end])
del self.lines[start-1:end]
self.current_code = '\n'.join(self.lines)
self._save_history('delete', (start, end), deleted)
return {
'success': True,
'message': f'✅ 已删除第{start}-{end}行 (共{end-start+1}行)',
'deleted': deleted
}
def find_replace(self, find_text: str, replace_text: str, max_replace: int = -1) -> Dict:
"""查找替换"""
if not self.is_loaded:
return {'success': False, 'error': '❌ 请先输入代码'}
old_code = self.current_code
count = 0
changes = []
for i, line in enumerate(self.lines):
if find_text in line:
if max_replace == -1 or count < max_replace:
old_line = line
new_line = line.replace(find_text, replace_text)
self.lines[i] = new_line
count += 1
changes.append(f"第{i+1}行: {old_line}{new_line}")
if count > 0:
self.current_code = '\n'.join(self.lines)
self._save_history('find_replace', find_text, old_code)
return {
'success': True,
'message': f'✅ 已替换 {count} 处',
'changes': '\n'.join(changes[:20]) # 最多显示20条
}
return {'success': False, 'error': f'❌ 未找到: {find_text}'}
def indent_lines(self, start: int, end: int, spaces: int = 4) -> Dict:
"""缩进调整"""
if not self.is_loaded:
return {'success': False, 'error': '❌ 请先输入代码'}
total = len(self.lines)
if not (1 <= start <= total and 1 <= end <= total):
return {'success': False, 'error': f'❌ 行号超出范围 (1-{total})'}
indent_str = ' ' * abs(spaces)
for i in range(start-1, end):
if spaces > 0:
self.lines[i] = indent_str + self.lines[i]
else:
# 反向缩进
self.lines[i] = self.lines[i].lstrip()[:abs(spaces)] + self.lines[i].lstrip()[abs(spaces):]
self.current_code = '\n'.join(self.lines)
self._save_history('indent', (start, end), None)
return {
'success': True,
'message': f'✅ 已调整第{start}-{end}行缩进 ({spaces} 空格)'
}
def comment_lines(self, start: int, end: int, uncomment: bool = False) -> Dict:
"""注释/取消注释"""
if not self.is_loaded:
return {'success': False, 'error': '❌ 请先输入代码'}
total = len(self.lines)
if not (1 <= start <= total and 1 <= end <= total):
return {'success': False, 'error': f'❌ 行号超出范围 (1-{total})'}
# 根据语言选择注释符
comment_map = {
'python': '#',
'javascript': '//',
'java': '//',
'cpp': '//',
'go': '//',
'php': '//',
'ruby': '#',
'rust': '//',
}
comment_symbol = comment_map.get(self.language, '#')
for i in range(start-1, end):
if uncomment:
self.lines[i] = self.lines[i].lstrip().lstrip(comment_symbol).lstrip()
else:
self.lines[i] = f"{comment_symbol} {self.lines[i]}"
self.current_code = '\n'.join(self.lines)
self._save_history('comment', (start, end), None)
action = '取消注释' if uncomment else '注释'
return {
'success': True,
'message': f'✅ 已{action}{start}-{end}行'
}
def _make_diff(self, old: str, new: str) -> str:
"""生成差异对比"""
diff = difflib.unified_diff(
old.split('\n'),
new.split('\n'),
lineterm='',
fromfile='修改前',
tofile='修改后'
)
return '\n'.join(diff)
def _save_history(self, operation: str, target, old_content):
"""保存历史"""
self.history.append({
'operation': operation,
'target': target,
'old': old_content
})
def undo(self) -> Dict:
"""撤销"""
if not self.history:
return {'success': False, 'error': '❌ 没有可撤销的操作'}
# 简单实现:重新加载原始代码
self.current_code = self.original_code
self.lines = self.original_code.split('\n')
self.history = []
return {'success': True, 'message': '✅ 已撤销所有修改'}
def get_current_code(self) -> str:
"""获取当前代码"""
return self.current_code
# ==================== UI界面 ====================
def create_ui():
engine = CodeGPSEngine()
# 主题色
theme = gr.themes.Soft(
primary_hue="purple",
secondary_hue="blue",
)
with gr.Blocks(title="CodeGPS Pro", theme=theme) as app:
gr.HTML("""
<div style="text-align:center; background:linear-gradient(135deg,#667eea,#764ba2); color:white; padding:40px; border-radius:15px; margin-bottom:30px;">
<h1 style="font-size:3rem; margin:0;">🎯 CodeGPS Pro</h1>
<p style="font-size:1.3rem; opacity:0.95; margin-top:10px;">大模型的精准代码手术刀 - 立即可用</p>
</div>
""")
# 状态显示
status_display = gr.Textbox(label="📊 状态", value="等待输入代码...", interactive=False, max_lines=1)
with gr.Tabs():
# ========== Tab 1: 代码编辑器 ==========
with gr.TabItem("📝 代码编辑器"):
with gr.Row():
with gr.Column(scale=1):
code_input = gr.Code(
label="代码输入区",
language="python",
lines=30
)
with gr.Row():
load_example_btn = gr.Button("📥 加载示例代码", variant="secondary")
clear_code_btn = gr.Button("🗑️ 清空", variant="secondary")
with gr.Column(scale=1):
gr.Markdown("### 🗺️ 代码结构")
structure_output = gr.Textbox(
label="",
lines=30,
interactive=False,
show_label=False
)
# ========== Tab 2: 精准查询 ==========
with gr.TabItem("🔍 查询代码"):
gr.Markdown("""
### 使用方法
- **查询单行**: 输入行号,如 `15`
- **查询范围**: 输入 `10-20` 或分别输入起止行号
- **查询函数**: 输入函数名,如 `main`
- **查询类**: 输入类名
- **搜索文本**: 输入关键词搜索
""")
with gr.Row():
with gr.Column():
gr.Markdown("#### 快捷查询")
with gr.Group():
gr.Markdown("**查询单行**")
query_line_num = gr.Number(label="行号", value=1, precision=0)
query_line_btn = gr.Button("查询单行", variant="primary")
with gr.Group():
gr.Markdown("**查询范围**")
with gr.Row():
query_start = gr.Number(label="起始行", value=1, precision=0)
query_end = gr.Number(label="结束行", value=10, precision=0)
query_range_btn = gr.Button("查询范围", variant="primary")
with gr.Group():
gr.Markdown("**查询函数/类**")
query_name = gr.Textbox(label="名称", placeholder="如: main")
with gr.Row():
query_func_btn = gr.Button("查询函数")
query_class_btn = gr.Button("查询类")
with gr.Group():
gr.Markdown("**搜索文本**")
search_keyword = gr.Textbox(label="关键词", placeholder="如: print")
search_btn = gr.Button("搜索", variant="primary")
with gr.Column():
query_result = gr.Textbox(
label="📍 查询结果",
lines=35,
interactive=False,
elem_classes="code-display"
)
# ========== Tab 3: 精准修改 ==========
with gr.TabItem("✏️ 修改代码"):
with gr.Row():
with gr.Column():
modify_mode = gr.Radio(
label="修改模式",
choices=[
"替换单行",
"替换多行",
"插入代码",
"删除行",
"查找替换",
"调整缩进",
"添加注释"
],
value="替换单行"
)
# 通用参数
with gr.Group() as single_line_group:
modify_line = gr.Number(label="目标行号", value=1, precision=0)
with gr.Group(visible=False) as range_group:
with gr.Row():
modify_start = gr.Number(label="起始行", value=1, precision=0)
modify_end = gr.Number(label="结束行", value=1, precision=0)
with gr.Group(visible=False) as find_replace_group:
find_text = gr.Textbox(label="查找文本")
replace_text = gr.Textbox(label="替换为")
with gr.Group(visible=False) as indent_group:
indent_spaces = gr.Slider(-8, 8, value=4, step=4, label="缩进空格数(负数为反向)")
new_code_input = gr.Code(
label="新代码内容",
language="python",
lines=10
)
with gr.Row():
execute_modify_btn = gr.Button("🚀 执行修改", variant="primary", scale=2)
undo_btn = gr.Button("↩️ 撤销全部", variant="secondary", scale=1)
with gr.Column():
modify_result = gr.Textbox(
label="📊 修改结果",
lines=10,
interactive=False
)
diff_output = gr.Textbox(
label="🔄 变更对比",
lines=25,
interactive=False,
elem_classes="code-display"
)
# ========== Tab 4: 导出与预览 ==========
with gr.TabItem("📤 导出代码"):
with gr.Column():
gr.Markdown("### 当前完整代码")
refresh_btn = gr.Button("🔄 刷新预览", variant="primary")
current_code_output = gr.Code(
label="",
language="python",
lines=40,
show_label=False
)
with gr.Row():
download_btn = gr.File(label="下载代码文件")
gr.Markdown("### 📊 修改统计")
stats_output = gr.Textbox(
label="",
lines=5,
interactive=False
)
# ==================== 事件绑定 ====================
# 自动加载代码(核心)
def auto_load_handler(code):
status = engine.auto_load(code)
structure = engine.list_all() if engine.is_loaded else ""
return status, structure
code_input.change(
fn=auto_load_handler,
inputs=[code_input],
outputs=[status_display, structure_output]
)
# 加载示例
def load_example():
example = '''def calculate_sum(a, b):
"""计算两数之和"""
return a + b
def calculate_average(numbers):
"""计算平均值"""
if not numbers:
return 0
return sum(numbers) / len(numbers)
class Calculator:
def __init__(self):
self.result = 0
def add(self, value):
"""加法"""
self.result += value
return self.result
def subtract(self, value):
"""减法"""
self.result -= value
return self.result
def reset(self):
"""重置"""
self.result = 0
if __name__ == "__main__":
calc = Calculator()
calc.add(10)
calc.add(5)
print(f"结果: {calc.result}")
'''
return example
load_example_btn.click(
fn=load_example,
outputs=[code_input]
)
clear_code_btn.click(
fn=lambda: "",
outputs=[code_input]
)
# 查询功能
def query_line(line_num):
result = engine.get_line(int(line_num))
if result['success']:
return result['formatted']
return result['error']
query_line_btn.click(
fn=query_line,
inputs=[query_line_num],
outputs=[query_result]
)
def query_range(start, end):
result = engine.get_lines(int(start), int(end))
if result['success']:
return result['formatted']
return result['error']
query_range_btn.click(
fn=query_range,
inputs=[query_start, query_end],
outputs=[query_result]
)
def query_function(name):
result = engine.get_function(name)
if result['success']:
return result['formatted']
return result['error']
query_func_btn.click(
fn=query_function,
inputs=[query_name],
outputs=[query_result]
)
def query_class(name):
result = engine.get_class(name)
if result['success']:
return result['formatted']
return result['error']
query_class_btn.click(
fn=query_class,
inputs=[query_name],
outputs=[query_result]
)
def search_text_handler(keyword):
result = engine.search_text(keyword)
if result['success']:
return result['formatted']
return result['error']
search_btn.click(
fn=search_text_handler,
inputs=[search_keyword],
outputs=[query_result]
)
# 修改模式切换
def update_modify_ui(mode):
if mode in ["替换多行", "删除行", "调整缩进", "添加注释"]:
return (
gr.update(visible=False), # single_line_group
gr.update(visible=True), # range_group
gr.update(visible=False), # find_replace_group
gr.update(visible=mode=="调整缩进"), # indent_group
gr.update(visible=mode not in ["删除行", "调整缩进", "添加注释"]) # new_code_input
)
elif mode == "查找替换":
return (
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False)
)
else: # 替换单行, 插入代码
return (
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=True)
)
modify_mode.change(
fn=update_modify_ui,
inputs=[modify_mode],
outputs=[single_line_group, range_group, find_replace_group, indent_group, new_code_input]
)
# 执行修改
def execute_modify(mode, line, start, end, new_code, find, replace, indent):
result = None
if mode == "替换单行":
result = engine.replace_line(int(line), new_code)
elif mode == "替换多行":
result = engine.replace_lines(int(start), int(end), new_code)
elif mode == "插入代码":
result = engine.insert_after(int(line), new_code)
elif mode == "删除行":
result = engine.delete_lines(int(start), int(end))
elif mode == "查找替换":
result = engine.find_replace(find, replace)
elif mode == "调整缩进":
result = engine.indent_lines(int(start), int(end), int(indent))
elif mode == "添加注释":
result = engine.comment_lines(int(start), int(end))
if result and result['success']:
# 更新代码编辑器
new_full_code = engine.get_current_code()
status = engine.auto_load(new_full_code)
diff = result.get('diff', result.get('changes', ''))
return (
result.get('message', '✅ 操作成功'),
diff,
new_full_code,
status
)
return (
result.get('error', '❌ 操作失败') if result else '❌ 未知错误',
'',
code_input.value,
status_display.value
)
execute_modify_btn.click(
fn=execute_modify,
inputs=[
modify_mode,
modify_line,
modify_start,
modify_end,
new_code_input,
find_text,
replace_text,
indent_spaces
],
outputs=[modify_result, diff_output, code_input, status_display]
)
# 撤销
def undo_handler():
result = engine.undo()
if result['success']:
code = engine.get_current_code()
status = engine.auto_load(code)
return result['message'], code, status
return result['error'], code_input.value, status_display.value
undo_btn.click(
fn=undo_handler,
outputs=[modify_result, code_input, status_display]
)
# 刷新预览
def refresh_preview():
code = engine.get_current_code()
stats = f"总行数: {len(engine.lines)}\n修改次数: {len(engine.history)}\n语言: {engine.language}"
return code, stats
refresh_btn.click(
fn=refresh_preview,
outputs=[current_code_output, stats_output]
)
return app
if __name__ == "__main__":
app = create_ui()
app.launch(server_name="0.0.0.0", server_port=7860, share=True)