Spaces:
Running
Running
| 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("'", "'").replace('"', '"') | |
| 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("'", "'").replace('"', '"') | |
| 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("'", "'").replace('"', '"') | |
| 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 | |
| ) | |