Spaces:
Running
Running
| from tkinter import Label | |
| import gradio as gr | |
| import logging | |
| 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 code_kit.agent_code_generator import code_generation_agent | |
| from code_kit.agent_style_generator import generate_random_style | |
| from code_kit.code_examples import CODE_EXAMPLES | |
| # Configure logging | |
| logger = logging.getLogger(__name__) | |
| def refresh_preview(code_type_display_name, current_code, chatbot_history): | |
| """ | |
| Refresh the preview and add a log entry. | |
| Refactored to rely solely on the HTML content for preview, assuming all frameworks | |
| produce a single-file HTML output that works in an iframe. | |
| """ | |
| logger.info(f"--- [Manual Refresh] Start ---") | |
| logger.info(f"Code Type: {code_type_display_name}") | |
| # Simple validation: Check if code seems to be HTML | |
| if not current_code or not isinstance(current_code, str): | |
| chatbot_history.append({"role": "assistant", "content": "⚠️ **警告**: 没有代码可供刷新。"}) | |
| return gr.update(), chatbot_history | |
| # For all currently supported frameworks (Static, React+Tailwind, R3F, Old School), | |
| # the output is a self-contained HTML string. | |
| # So we can process them uniformly. | |
| 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 preview.") | |
| return gr.HTML(final_preview_html), 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(): | |
| # Inject custom CSS to hide components | |
| gr.HTML(""" | |
| <style> | |
| .hidden-component { | |
| display: none; | |
| } | |
| </style> | |
| """) | |
| fullscreen_state = gr.State(False) | |
| 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="提示词") | |
| overall_style_input = gr.Textbox(label="整体风格", placeholder="例如:类似一本羊皮纸的书", lines=2) | |
| decoration_input = gr.Textbox(label="装饰风格", placeholder="例如:粗边框,无圆角", lines=2) | |
| # Hidden textbox to store the raw palette string for the prompt | |
| palette_input = gr.Textbox(label="Palette (Raw)", elem_classes="hidden-component") | |
| palette_display = gr.HTML(value='<div style="color: #999; font-size: 12px;">(生成风格后展示)</div>', | |
| container=True, | |
| label="风格色板", | |
| show_label=True) | |
| generate_style_btn = gr.Button("🎲 随机生成风格", size="sm") | |
| with gr.Column(): | |
| gr.Markdown("### 示例") | |
| examples_dataset = gr.Dataset( | |
| components=[gr.Textbox(visible=False)], | |
| samples=[[item["task"]] for item in CODE_EXAMPLES], | |
| label="示例", | |
| headers=["选择一个示例"], | |
| ) | |
| 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) | |
| js_error_channel = gr.Textbox(visible=True, elem_classes=["js_error_channel"], label="Debug Error Channel", interactive=False) | |
| # Event Handler for Example Selection | |
| def on_select_example(evt: gr.SelectData): | |
| selected_task = evt.value[0] | |
| item = next((i for i in CODE_EXAMPLES if i["task"] == selected_task), None) | |
| if not item: | |
| return gr.update(), gr.update() | |
| return gr.update(value=item["user_prompt"]), gr.update(value=item["model"]) | |
| examples_dataset.select( | |
| fn=on_select_example, | |
| inputs=None, | |
| outputs=[prompt_input, model_choice_dropdown], | |
| show_progress="none" | |
| ) | |
| # Event Handler for Style Generation | |
| generate_style_btn.click( | |
| fn=generate_random_style, | |
| inputs=[model_choice_dropdown], | |
| outputs=[palette_display, palette_input, decoration_input, overall_style_input] | |
| ) | |
| refresh_button.click( | |
| fn=refresh_preview, | |
| inputs=[code_framework_dropdown, code_output, log_chatbot], | |
| outputs=[preview_output, log_chatbot] | |
| ) | |
| generate_button.click( | |
| fn=code_generation_agent, | |
| inputs=[ | |
| code_framework_dropdown, | |
| model_choice_dropdown, | |
| prompt_input, | |
| palette_input, # Pass the raw palette string | |
| decoration_input, | |
| overall_style_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 | |
| ) | |