ling-series-spaces / tab_code.py
GitHub Action
Sync ling-space changes from GitHub commit 86dd25a
74ebe5c
raw
history blame
10.9 kB
import gradio as gr
import time
import logging
from model_handler import ModelHandler
from config import CHAT_MODEL_SPECS, LING_1T, CODE_FRAMEWORK_SPECS, STATIC_PAGE
from ui_components.model_selector import create_model_selector
from ui_components.code_framework_selector import create_code_framework_selector
from tab_code_prompts.html_system_prompt import get_html_system_prompt
# Configure logging
logger = logging.getLogger(__name__)
# Read the content of the JavaScript file for error catching
try:
with open("static/catch-error.js", "r", encoding="utf-8") as f:
CATCH_ERROR_JS_SCRIPT = f.read()
except FileNotFoundError:
logger.error("Error: static/catch-error.js not found. The error catching overlay will not work.")
CATCH_ERROR_JS_SCRIPT = ""
def get_spinner_html():
"""Return HTML with a CSS spinner animation"""
return """
<div style="width: 100%; height: 600px; display: flex; justify-content: center; align-items: center; border: 1px solid #ddd; background-color: #f9f9f9;">
<div class="spinner"></div>
</div>
<style>
.spinner {
border: 4px solid rgba(0, 0, 0, 0.1);
width: 36px;
height: 36px;
border-radius: 50%;
border-left-color: #09f;
animation: spin 1s ease infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
"""
def generate_code(code_type, model_choice, user_prompt, chatbot_history):
"""Generate code and provide a preview, updating a log stream chatbot."""
logger.info(f"--- [Code Generation] Start ---")
logger.info(f"Code Type: {code_type}, Model: {model_choice}, Prompt: '{user_prompt}'")
if not user_prompt:
chatbot_history.append({"role": "assistant", "content": "🚨 **错误**: 请输入提示词。"})
yield "", gr.update(value="<p>预览将在此处显示。</p>"), chatbot_history, gr.update()
return
chatbot_history.append({"role": "assistant", "content": "⏳ 开始生成代码..."})
yield "", gr.HTML(get_spinner_html()), chatbot_history, gr.update()
if user_prompt == "create an error":
error_code = f"""<h1>This will create an error</h1><script>{CATCH_ERROR_JS_SCRIPT}</script><script>nonExistentFunction();</script>""";
escaped_code = error_code.replace("'", "&apos;").replace('"', '&quot;')
final_preview_html = f"""
<div style="width: 100%; height: 600px; border: 1px solid #ddd; overflow: hidden; position: relative; background-color: #f9f9f9;">
<iframe srcdoc='{escaped_code}'
style="position: absolute; top: 0; left: 0; width: 200%; height: 200%; transform: scale(0.5); transform-origin: 0 0; border: none;">
</iframe>
</div>
"""
chatbot_history.append({"role": "assistant", "content": "✅ **成功**: 已生成一个用于测试的错误页面。"})
yield error_code, gr.update(value=final_preview_html), chatbot_history, gr.Tabs(selected=0)
return
start_time = time.time()
model_handler = ModelHandler()
if code_type == "静态页面":
system_prompt = get_html_system_prompt()
full_code_with_think = ""
full_code_for_preview = ""
buffer = ""
is_thinking = False
for code_chunk in model_handler.generate_code(system_prompt, user_prompt, model_choice):
full_code_with_think += code_chunk
buffer += code_chunk
while True:
if is_thinking:
end_index = buffer.find("</think>")
if end_index != -1:
is_thinking = False
buffer = buffer[end_index + len("</think>"):]
else:
break
else:
start_index = buffer.find("<think>")
if start_index != -1:
part_to_add = buffer[:start_index]
full_code_for_preview += part_to_add
is_thinking = True
buffer = buffer[start_index:]
else:
full_code_for_preview += buffer
buffer = ""
break
elapsed_time = time.time() - start_time
generated_length = len(full_code_with_think)
speed = generated_length / elapsed_time if elapsed_time > 0 else 0
log_message = f"""
**⏳ 正在生成中...**
- **时间:** {elapsed_time:.2f}s
- **长度:** {generated_length} chars
- **速度:** {speed:.2f} char/s
"""
if len(chatbot_history) > 0 and "正在生成中" in chatbot_history[-1]["content"]:
chatbot_history[-1] = {"role": "assistant", "content": log_message}
else:
chatbot_history.append({"role": "assistant", "content": log_message})
yield full_code_with_think, gr.update(), chatbot_history, gr.update()
escaped_code = full_code_for_preview.replace("'", "&apos;").replace('"', '&quot;')
final_preview_html = f"""
<div style="width: 100%; height: 600px; border: 1px solid #ddd; overflow: hidden; position: relative; background-color: #f9f9f9;">
<iframe srcdoc='{escaped_code}'
style="position: absolute; top: 0; left: 0; width: 200%; height: 200%; transform: scale(0.5); transform-origin: 0 0; border: none;">
</iframe>
</div>
"""
chatbot_history.append({"role": "assistant", "content": "✅ **成功**: 代码生成完成!"})
yield full_code_with_think, gr.HTML(final_preview_html), chatbot_history, gr.Tabs(selected=0)
logger.info("Static page streaming finished.")
def refresh_preview(code_type, current_code, chatbot_history):
"""Refresh the preview and add a log entry."""
logger.info(f"--- [Manual Refresh] Start ---")
logger.info(f"Code Type: {code_type}")
if code_type == "静态页面":
escaped_code = current_code.replace("'", "&apos;").replace('"', '&quot;')
final_preview_html = f"""
<div style="width: 100%; height: 600px; border: 1px solid #ddd; overflow: hidden; position: relative; background-color: #f9f9f9;">
<iframe srcdoc='{escaped_code}'
style="position: absolute; top: 0; left: 0; width: 200%; height: 200%; transform: scale(0.5); transform-origin: 0 0; border: none;">
</iframe>
</div>
"""
chatbot_history.append({"role": "assistant", "content": "🔄 **状态**: 预览已手动刷新。"})
logger.info("Refreshed static page preview.")
return gr.HTML(final_preview_html), chatbot_history
chatbot_history.append({"role": "assistant", "content": "⚠️ **警告**: 未知的代码类型,无法刷新。"})
return gr.update(), chatbot_history
def toggle_fullscreen(is_fullscreen):
is_fullscreen = not is_fullscreen
new_button_text = "退出全屏" if is_fullscreen else "全屏预览"
panel_visibility = not is_fullscreen
return is_fullscreen, gr.update(value=new_button_text), gr.update(visible=panel_visibility)
def log_js_error(error_text, chatbot_history):
"""Appends a JavaScript error received from the frontend to the log chatbot."""
if not error_text:
return chatbot_history
formatted_error = f"🚨 **在预览中发现运行时异常!**\n```\n{error_text}\n```"
# Check if the last message is the same error to prevent flooding
if chatbot_history and chatbot_history[-1]["content"] == formatted_error:
return chatbot_history
chatbot_history.append({"role": "assistant", "content": formatted_error})
return chatbot_history
def create_code_tab():
fullscreen_state = gr.State(False)
html_examples = [
"Write a hello world alert",
"Create a Canvas animation of continuous colorful fireworks blooming on a black background.",
"Generate a Canvas special effect with iridescent light streams.",
"create an error"
]
with gr.Row(elem_id="indicator-code-tab"):
with gr.Column(scale=1) as left_panel:
with gr.Column(scale=1): # Settings Panel
code_framework_dropdown = create_code_framework_selector(
framework_specs=CODE_FRAMEWORK_SPECS,
default_framework_constant=STATIC_PAGE
)
model_choice_dropdown, model_description_markdown = create_model_selector(
model_specs=CHAT_MODEL_SPECS,
default_model_constant=LING_1T
)
prompt_input = gr.Textbox(lines=5, placeholder="例如:创建一个带标题和按钮的简单页面", label="提示词")
with gr.Column():
gr.Examples(examples=html_examples, inputs=prompt_input, label="✨ 试试这些酷炫的例子吧")
generate_button = gr.Button("生成代码", variant="primary")
with gr.Column(scale=4):
with gr.Tabs(elem_id="result_tabs") as result_tabs:
with gr.TabItem("实时预览", id=0):
with gr.Row():
gr.Markdown("### 实时预览")
fullscreen_button = gr.Button("全屏预览", scale=0)
preview_output = gr.HTML(value="<p>预览将在此处显示。</p>")
with gr.TabItem("生成的源代码", id=1):
gr.Markdown("### 生成的源代码")
code_output = gr.Code(language="html", label="生成的代码", interactive=True)
refresh_button = gr.Button("刷新预览")
with gr.Column(scale=1):
log_chatbot = gr.Chatbot(label="生成日志", height=300, type="messages")
js_error_channel = gr.Textbox(visible=True, elem_classes=["js_error_channel"], label="Debug Error Channel", interactive=False)
refresh_button.click(
fn=refresh_preview,
inputs=[code_framework_dropdown, code_output, log_chatbot],
outputs=[preview_output, log_chatbot]
)
generate_button.click(
fn=generate_code,
inputs=[code_framework_dropdown, model_choice_dropdown, prompt_input, log_chatbot],
outputs=[code_output, preview_output, log_chatbot, result_tabs]
)
fullscreen_button.click(
fn=toggle_fullscreen,
inputs=[fullscreen_state],
outputs=[fullscreen_state, fullscreen_button, left_panel]
)
js_error_channel.change(
fn=log_js_error,
inputs=[js_error_channel, log_chatbot],
outputs=log_chatbot
)