import gradio as gr import time import logging import os import re from model_handler import ModelHandler from config import CODE_FRAMEWORK_SPECS, STATIC_PAGE from tab_code_prompts.framework_system_prompts import get_framework_system_prompt from code_kit.code_trimmer import trim_html_markdown_blocks # Import the trimmer # 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 """
""" def inject_error_catcher(html_code): """ Injects the error catching script into the HTML code. It tries to insert it after the tag. If is not found, it prepends it to the code (fallback). """ if not CATCH_ERROR_JS_SCRIPT: return html_code script_tag = f"" # Try to find (case insensitive) head_match = re.search(r"", html_code, re.IGNORECASE) if head_match: # Insert after insert_pos = head_match.end() return html_code[:insert_pos] + script_tag + html_code[insert_pos:] # Try to find (case insensitive) body_match = re.search(r"", html_code, re.IGNORECASE) if body_match: # Insert before insert_pos = body_match.start() return html_code[:insert_pos] + script_tag + html_code[insert_pos:] # Fallback: Just prepend if it looks somewhat like HTML or empty return script_tag + html_code def code_generation_agent(code_type_display_name, model_choice, user_prompt, color_palette, decoration_style, overall_style, chatbot_history): """Generate code and provide a preview, updating a log stream chatbot.""" logger.info(f"--- [Code Generation] Start ---") logger.info(f"Code Type (Display Name): {code_type_display_name}, Model: {model_choice}, Prompt: '{user_prompt}'") # Map display name back to constant key code_framework_key = next((k for k, v in CODE_FRAMEWORK_SPECS.items() if v["display_name"] == code_type_display_name), STATIC_PAGE) logger.info(f"Mapped Code Framework Key: {code_framework_key}") if not user_prompt: chatbot_history.append({"role": "assistant", "content": "🚨 **错误**: 请输入提示词。"}) yield "", gr.update(value="

预览将在此处显示。

"), 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" or user_prompt == "创建一个报错示例": error_code = f"""

This will create an error

""" escaped_code = error_code.replace("'", "'").replace('"', """) final_preview_html = f"""
""" chatbot_history.append({"role": "assistant", "content": "✅ **成功**: 已生成一个用于测试的错误页面。"}) yield error_code, gr.update(value=final_preview_html), chatbot_history, gr.Tabs(selected=0) return # --- Append Style Prompt --- full_user_prompt = user_prompt if color_palette or decoration_style or overall_style: full_user_prompt += "\n\n--- Visual Style Requirements (Strictly Follow These) ---\n" if overall_style: full_user_prompt += f"Overall Theme/Vibe: {overall_style}\n" if decoration_style: full_user_prompt += f"UI Decoration Style: {decoration_style}\n" if color_palette: full_user_prompt += f"Color Palette Mapping (Use these colors for their assigned roles): {color_palette}\n" logger.info(f"Full Prompt with Style: {full_user_prompt}") # --------------------------- start_time = time.time() model_handler = ModelHandler() # Get the system prompt based on the selected framework AND specific color palette # Passing color_palette allows it to be baked into the system prompt instructions system_prompt = get_framework_system_prompt(code_framework_key, color_palette) full_code_with_think = "" full_code_for_preview = "" buffer = "" is_thinking = False for code_chunk in model_handler.generate_code(system_prompt, full_user_prompt, model_choice): full_code_with_think += code_chunk buffer += code_chunk while True: if is_thinking: end_index = buffer.find("") if end_index != -1: is_thinking = False buffer = buffer[end_index + len(""):] else: break else: start_index = buffer.find("") 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() # Apply trimming to the final generated code trimmed_full_code_with_think = trim_html_markdown_blocks(full_code_with_think) trimmed_full_code_for_preview = trim_html_markdown_blocks(full_code_for_preview) # Inject error catching script before final render final_code_with_error_catcher = inject_error_catcher(trimmed_full_code_for_preview) escaped_code = final_code_with_error_catcher.replace("'", "'").replace('"', """) final_preview_html = f"""
""" chatbot_history.append({"role": "assistant", "content": "✅ **成功**: 代码生成完成!"}) # Yield the trimmed code for display in the "Generated Source Code" tab yield trimmed_full_code_with_think, gr.HTML(final_preview_html), chatbot_history, gr.Tabs(selected=0) logger.info("Code generation streaming finished.")