Spaces:
Running
Running
| import gradio as gr | |
| import logging | |
| from config import CHAT_MODEL_SPECS, LING_1T, CODE_FRAMEWORK_SPECS, REACT_TAILWIND | |
| 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 | |
| from i18n import get_text | |
| # Configure logging | |
| logger = logging.getLogger(__name__) | |
| def refresh_preview(code_type_display_name, current_code, chatbot_history, lang): | |
| """ | |
| 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": get_text('code_log_no_code_warning', lang)}) | |
| 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": get_text('code_log_refreshed_state', lang)}) | |
| logger.info("Refreshed preview.") | |
| return gr.HTML(final_preview_html), chatbot_history | |
| def toggle_fullscreen(is_fullscreen, lang): | |
| logger.info(f"--- [Toggle Fullscreen] Called ---") | |
| logger.info(f"Current state before toggle: {is_fullscreen}") | |
| is_fullscreen = not is_fullscreen | |
| new_button_text = get_text('code_exit_fullscreen_button', lang) if is_fullscreen else get_text('code_fullscreen_button', lang) | |
| logger.info(f"New state: {is_fullscreen}") | |
| return ( | |
| is_fullscreen, | |
| gr.update(value=new_button_text) | |
| ) | |
| def log_js_error(error_text, chatbot_history, lang): | |
| """Appends a JavaScript error received from the frontend to the log chatbot.""" | |
| if not error_text: | |
| return chatbot_history | |
| formatted_error = get_text('code_log_runtime_error', lang).format(error_text=error_text) | |
| # 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(initial_lang: str, current_lang_state: gr.State): | |
| gr.HTML("""<style>.hidden-component { display: none; }</style>""") | |
| fullscreen_state = gr.State(False) | |
| # JavaScript to dispatch a custom event for fullscreen toggle | |
| # Instead of relying on the potentially stale 'is_fullscreen' state passed from Python, | |
| # we check the actual DOM state of the panel to decide what to do. | |
| dispatch_fullscreen_event_js = """ | |
| () => { | |
| const left = document.getElementById('left-panel'); | |
| let isCurrentlyVisible = true; | |
| if (left) { | |
| // Check if hidden class is present or display is none | |
| // Note: We verify both class and inline style to be safe | |
| const isHidden = left.classList.contains('hidden-panel') || window.getComputedStyle(left).display === 'none'; | |
| isCurrentlyVisible = !isHidden; | |
| } | |
| // Logic: | |
| // If panels are currently VISIBLE -> We want to enter Fullscreen (True) | |
| // If panels are currently HIDDEN -> We want to exit Fullscreen (False) | |
| const targetState = isCurrentlyVisible; | |
| console.log("DOM Check - Panel Visible:", isCurrentlyVisible, "-> Target Fullscreen:", targetState); | |
| window.dispatchEvent(new CustomEvent('tab.code.toggleFullscreen', { detail: { isFullscreen: targetState } })); | |
| return targetState; | |
| } | |
| """ | |
| with gr.Row(elem_id="indicator-code-tab"): | |
| with gr.Column(scale=1, visible=True, elem_id="left-panel") 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=REACT_TAILWIND | |
| ) | |
| model_choice_dropdown, model_description_markdown = create_model_selector( | |
| model_specs=CHAT_MODEL_SPECS, | |
| default_model_constant=LING_1T, | |
| lang_state=current_lang_state, | |
| initial_lang=initial_lang | |
| ) | |
| prompt_input = gr.Textbox(lines=5, placeholder=get_text('code_prompt_placeholder', initial_lang), label=get_text('code_prompt_label', initial_lang)) | |
| overall_style_input = gr.Textbox(label=get_text('code_overall_style_label', initial_lang), placeholder=get_text('code_overall_style_placeholder', initial_lang), lines=2) | |
| decoration_input = gr.Textbox(label=get_text('code_decoration_style_label', initial_lang), placeholder=get_text('code_decoration_style_placeholder', initial_lang), lines=2) | |
| palette_input = gr.Textbox(label="Palette (Raw)", elem_classes="hidden-component") | |
| palette_display = gr.HTML(value=f"<div style='color: #999; font-size: 12px;'>{get_text('code_palette_placeholder', initial_lang)}</div>", container=True, label=get_text('code_palette_label', initial_lang), show_label=True) | |
| generate_style_btn = gr.Button(get_text('code_generate_style_button', initial_lang), size="sm") | |
| with gr.Column(): | |
| examples_title = gr.Markdown(get_text('code_examples_title', initial_lang)) | |
| examples_dataset = gr.Dataset( | |
| components=[gr.Textbox(visible=False)], | |
| samples=[[item["task"]] for item in CODE_EXAMPLES], | |
| label=get_text('code_examples_dataset_label', initial_lang), | |
| headers=[get_text('code_examples_dataset_header', initial_lang)], | |
| ) | |
| generate_button = gr.Button(get_text('code_generate_code_button', initial_lang), variant="primary") | |
| with gr.Column(scale=4): | |
| with gr.Tabs(elem_id="result_tabs") as result_tabs: | |
| with gr.TabItem(get_text('code_preview_tab_title', initial_lang), id=0) as preview_tab: | |
| with gr.Row(): | |
| preview_header = gr.Markdown(get_text('code_preview_header', initial_lang)) | |
| fullscreen_button = gr.Button(get_text('code_fullscreen_button', initial_lang), scale=0) | |
| preview_output = gr.HTML(value=get_text('code_preview_placeholder', initial_lang)) | |
| with gr.TabItem(get_text('code_source_code_tab_title', initial_lang), id=1) as source_code_tab: | |
| source_code_header = gr.Markdown(get_text('code_source_code_header', initial_lang)) | |
| code_output = gr.Code(language="html", label=get_text('code_source_code_label', initial_lang), interactive=True) | |
| refresh_button = gr.Button(get_text('code_refresh_preview_button', initial_lang)) | |
| with gr.Column(scale=1, elem_id="right-panel") as right_panel: | |
| log_chatbot = gr.Chatbot(label=get_text('code_log_chatbot_label', initial_lang), height=300) | |
| js_error_channel = gr.Textbox(visible=True, elem_classes=["js_error_channel"], label=get_text('code_debug_channel_label', initial_lang), 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="hidden" | |
| ) | |
| # 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, current_lang_state], | |
| 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, current_lang_state], | |
| outputs=[fullscreen_state, fullscreen_button], | |
| js=dispatch_fullscreen_event_js | |
| ) | |
| js_error_channel.change( | |
| fn=log_js_error, | |
| inputs=[js_error_channel, log_chatbot, current_lang_state], | |
| outputs=log_chatbot | |
| ) | |
| return { | |
| "prompt_input": prompt_input, "overall_style_input": overall_style_input, | |
| "decoration_input": decoration_input, "palette_display": palette_display, | |
| "generate_style_btn": generate_style_btn, "examples_title": examples_title, | |
| "examples_dataset": examples_dataset, "generate_button": generate_button, | |
| "preview_tab": preview_tab, "preview_header": preview_header, | |
| "fullscreen_button": fullscreen_button, "preview_output": preview_output, | |
| "source_code_tab": source_code_tab, "source_code_header": source_code_header, | |
| "code_output": code_output, "refresh_button": refresh_button, | |
| "log_chatbot": log_chatbot, "js_error_channel": js_error_channel, | |
| "model_choice_dropdown": model_choice_dropdown, | |
| "model_description_markdown": model_description_markdown | |
| } | |
| def update_language(lang: str, components: dict): | |
| return { | |
| components["prompt_input"]: gr.update(label=get_text('code_prompt_label', lang), placeholder=get_text('code_prompt_placeholder', lang)), | |
| components["overall_style_input"]: gr.update(label=get_text('code_overall_style_label', lang), placeholder=get_text('code_overall_style_placeholder', lang)), | |
| components["decoration_input"]: gr.update(label=get_text('code_decoration_style_label', lang), placeholder=get_text('code_decoration_style_placeholder', lang)), | |
| components["palette_display"]: gr.update(label=get_text('code_palette_label', lang)), | |
| components["generate_style_btn"]: gr.update(value=get_text('code_generate_style_button', lang)), | |
| components["examples_title"]: gr.update(value=get_text('code_examples_title', lang)), | |
| components["examples_dataset"]: gr.update(label=get_text('code_examples_dataset_label', lang), headers=[get_text('code_examples_dataset_header', lang)]), | |
| components["generate_button"]: gr.update(value=get_text('code_generate_code_button', lang)), | |
| components["preview_tab"]: gr.update(label=get_text('code_preview_tab_title', lang)), | |
| components["preview_header"]: gr.update(value=get_text('code_preview_header', lang)), | |
| # Fullscreen button is updated in its own event handler | |
| components["source_code_tab"]: gr.update(label=get_text('code_source_code_tab_title', lang)), | |
| components["source_code_header"]: gr.update(value=get_text('code_source_code_header', lang)), | |
| components["code_output"]: gr.update(label=get_text('code_source_code_label', lang)), | |
| components["refresh_button"]: gr.update(value=get_text('code_refresh_preview_button', lang)), | |
| components["log_chatbot"]: gr.update(label=get_text('code_log_chatbot_label', lang)), | |
| components["js_error_channel"]: gr.update(label=get_text('code_debug_channel_label', lang)), | |
| } | |