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"""
""" 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("""""") 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"
{get_text('code_palette_placeholder', initial_lang)}
", 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)), }