| """ |
| Gradio user interface for AnyCoder. |
| Defines the main UI layout, components, and event handlers. |
| """ |
| import os |
| import gradio as gr |
| from typing import Dict, Optional |
| from huggingface_hub import HfApi |
| import httpx |
|
|
| |
| |
| _original_client_init = httpx.AsyncClient.__init__ |
|
|
| def _patched_client_init(self, *args, **kwargs): |
| |
| if 'timeout' not in kwargs: |
| kwargs['timeout'] = httpx.Timeout( |
| connect=30.0, |
| read=60.0, |
| write=30.0, |
| pool=30.0 |
| ) |
| return _original_client_init(self, *args, **kwargs) |
|
|
| httpx.AsyncClient.__init__ = _patched_client_init |
|
|
| from .config import ( |
| AVAILABLE_MODELS, DEFAULT_MODEL, DEFAULT_MODEL_NAME, |
| LANGUAGE_CHOICES, get_gradio_language |
| ) |
| from .themes import THEME_CONFIGS, get_saved_theme, current_theme |
| from .prompts import HTML_SYSTEM_PROMPT |
| from .models import history_to_chatbot_messages |
| from .parsers import ( |
| history_render, clear_history, create_multimodal_message, |
| parse_multipage_html_output, parse_transformers_js_output, |
| parse_react_output, format_transformers_js_output, |
| validate_and_autofix_files, parse_multi_file_python_output, |
| is_streamlit_code, is_gradio_code |
| ) |
| from .deploy import ( |
| check_authentication, update_ui_for_auth_status, |
| generation_code, deploy_to_spaces, add_anycoder_tag_to_readme, |
| _parse_repo_or_model_url, load_project_from_url, check_hf_space_url, |
| import_repo_to_app, extract_import_statements, |
| generate_requirements_txt_with_llm, prettify_comfyui_json_for_html, |
| get_trending_models, import_model_from_hf, get_trending_spaces, import_space_from_hf, |
| switch_model_code_type |
| ) |
| from .agent import ( |
| agent_generate_with_questions, agent_process_answers_and_generate |
| ) |
|
|
| |
| with gr.Blocks( |
| title="AnyCoder - AI Code Generator", |
| theme=current_theme, |
| css=""" |
| .theme-info { font-size: 0.9em; opacity: 0.8; } |
| .theme-description { padding: 8px 0; } |
| .theme-status { |
| padding: 10px; |
| border-radius: 8px; |
| background: rgba(34, 197, 94, 0.1); |
| border: 1px solid rgba(34, 197, 94, 0.2); |
| margin: 8px 0; |
| } |
| .restart-needed { |
| padding: 12px; |
| border-radius: 8px; |
| background: rgba(255, 193, 7, 0.1); |
| border: 1px solid rgba(255, 193, 7, 0.3); |
| margin: 8px 0; |
| text-align: center; |
| } |
| /* Authentication status styling */ |
| .auth-status { |
| padding: 8px 12px; |
| border-radius: 6px; |
| margin: 8px 0; |
| font-weight: 500; |
| text-align: center; |
| } |
| .auth-status:has-text("🔒") { |
| background: rgba(231, 76, 60, 0.1); |
| border: 1px solid rgba(231, 76, 60, 0.3); |
| color: #e74c3c; |
| } |
| .auth-status:has-text("✅") { |
| background: rgba(46, 204, 113, 0.1); |
| border: 1px solid rgba(46, 204, 113, 0.3); |
| color: #2ecc71; |
| } |
| /* App link styling (visible on all devices) */ |
| .app-link { |
| display: block; |
| padding: 12px; |
| border-radius: 8px; |
| background: rgba(59, 130, 246, 0.1); |
| border: 1px solid rgba(59, 130, 246, 0.3); |
| margin: 12px 0; |
| text-align: center; |
| } |
| """ |
| ) as demo: |
| history = gr.State([]) |
| setting = gr.State({ |
| "system": HTML_SYSTEM_PROMPT, |
| }) |
| current_model = gr.State(DEFAULT_MODEL) |
| open_panel = gr.State(None) |
| last_login_state = gr.State(None) |
| models_first_change = gr.State(True) |
| spaces_first_change = gr.State(True) |
| agent_mode_enabled = gr.State(False) |
| current_trending_model_id = gr.State("") |
| agent_conversation_state = gr.State({ |
| "stage": "initial", |
| "original_query": "", |
| "questions": "" |
| }) |
|
|
| with gr.Sidebar() as sidebar: |
| login_button = gr.LoginButton() |
| |
| |
| mobile_link = gr.HTML( |
| """ |
| <div class="app-link"> |
| 📱 <strong>Using Mobile?</strong><br/> |
| <a href="https://akhaliq-anycoder.hf.space" target="_blank" style="color: #007bff; text-decoration: underline;"> |
| Use the app here |
| </a> |
| </div> |
| """, |
| visible=True |
| ) |
|
|
|
|
| |
| import_header_md = gr.Markdown("📥 Import Project (Space, GitHub, or Model)", visible=False) |
| load_project_url = gr.Textbox( |
| label="Project URL", |
| placeholder="https://huggingface.co/spaces/user/space OR https://huggingface.co/user/model OR https://github.com/owner/repo", |
| lines=1 |
| , visible=False) |
| load_project_btn = gr.Button("📥 Import Project", variant="secondary", size="sm", visible=True) |
| load_project_status = gr.Markdown(visible=False) |
| |
| |
| trending_models_dropdown = gr.Dropdown( |
| label="🔥 Trending HuggingFace Models", |
| choices=[], |
| value=None, |
| interactive=True, |
| visible=True |
| ) |
| trending_models_status = gr.Markdown(visible=False) |
| switch_model_code_btn = gr.Button("🔄 Switch Code Type", visible=False, size="sm", variant="secondary") |
| |
| |
| trending_spaces_dropdown = gr.Dropdown( |
| label="🚀 Trending HuggingFace Spaces", |
| choices=[], |
| value=None, |
| interactive=True, |
| visible=True |
| ) |
| trending_spaces_status = gr.Markdown(visible=False) |
| |
| |
| chat_history = gr.Chatbot( |
| label="Conversation History", |
| type="messages", |
| height=300, |
| show_copy_button=True, |
| visible=True |
| ) |
| |
| |
| input = gr.Textbox( |
| label="What would you like to build?", |
| placeholder="🔒 Please log in with Hugging Face to use AnyCoder...", |
| lines=2, |
| visible=True, |
| interactive=False |
| ) |
| |
| language_choices = [ |
| "html", "gradio", "transformers.js", "streamlit", "comfyui", "react" |
| ] |
| language_dropdown = gr.Dropdown( |
| choices=language_choices, |
| value="html", |
| label="Code Language", |
| visible=True |
| ) |
| |
| |
| agent_mode_checkbox = gr.Checkbox( |
| label="🤖 Enable Agent Mode", |
| value=False, |
| info="Agent will ask follow-up questions and create a task list before coding", |
| visible=True |
| ) |
| |
| |
| with gr.Row(): |
| btn = gr.Button("Generate", variant="secondary", size="lg", scale=2, visible=True, interactive=False) |
| clear_btn = gr.Button("Clear", variant="secondary", size="sm", scale=1, visible=True) |
| |
| deploy_header_md = gr.Markdown("", visible=False) |
| deploy_btn = gr.Button("Publish", variant="primary", visible=True) |
| deploy_status = gr.Markdown(visible=False, label="Deploy status") |
| |
| |
|
|
| |
| model_dropdown = gr.Dropdown( |
| choices=[model['name'] for model in AVAILABLE_MODELS], |
| value=DEFAULT_MODEL_NAME, |
| label="Model", |
| visible=True |
| ) |
| provider_state = gr.State("auto") |
| |
| def on_model_change(model_name): |
| for m in AVAILABLE_MODELS: |
| if m['name'] == model_name: |
| return m |
| return AVAILABLE_MODELS[0] |
| def save_prompt(input): |
| return {setting: {"system": input}} |
| model_dropdown.change( |
| lambda model_name: on_model_change(model_name), |
| inputs=model_dropdown, |
| outputs=[current_model] |
| ) |
| |
| |
|
|
| with gr.Column() as main_column: |
| with gr.Tabs(): |
| with gr.Tab("Code"): |
| code_output = gr.Code( |
| language="html", |
| lines=25, |
| interactive=True, |
| label="Generated code" |
| ) |
| |
| |
| |
| |
| with gr.Group(visible=False) as tjs_group: |
| with gr.Tabs(): |
| with gr.Tab("index.html"): |
| tjs_html_code = gr.Code(language="html", lines=20, interactive=True, label="index.html") |
| with gr.Tab("index.js"): |
| tjs_js_code = gr.Code(language="javascript", lines=20, interactive=True, label="index.js") |
| with gr.Tab("style.css"): |
| tjs_css_code = gr.Code(language="css", lines=20, interactive=True, label="style.css") |
|
|
| |
| with gr.Group(visible=False) as python_group_2: |
| with gr.Tabs(): |
| with gr.Tab("app.py") as python_tab_2_1: |
| python_code_2_1 = gr.Code(language="python", lines=20, interactive=True, label="app.py") |
| with gr.Tab("file 2") as python_tab_2_2: |
| python_code_2_2 = gr.Code(language="python", lines=18, interactive=True, label="file 2") |
| |
| with gr.Group(visible=False) as python_group_3: |
| with gr.Tabs(): |
| with gr.Tab("app.py") as python_tab_3_1: |
| python_code_3_1 = gr.Code(language="python", lines=20, interactive=True, label="app.py") |
| with gr.Tab("file 2") as python_tab_3_2: |
| python_code_3_2 = gr.Code(language="python", lines=18, interactive=True, label="file 2") |
| with gr.Tab("file 3") as python_tab_3_3: |
| python_code_3_3 = gr.Code(language="python", lines=18, interactive=True, label="file 3") |
| |
| with gr.Group(visible=False) as python_group_4: |
| with gr.Tabs(): |
| with gr.Tab("app.py") as python_tab_4_1: |
| python_code_4_1 = gr.Code(language="python", lines=20, interactive=True, label="app.py") |
| with gr.Tab("file 2") as python_tab_4_2: |
| python_code_4_2 = gr.Code(language="python", lines=18, interactive=True, label="file 2") |
| with gr.Tab("file 3") as python_tab_4_3: |
| python_code_4_3 = gr.Code(language="python", lines=18, interactive=True, label="file 3") |
| with gr.Tab("file 4") as python_tab_4_4: |
| python_code_4_4 = gr.Code(language="python", lines=18, interactive=True, label="file 4") |
| |
| with gr.Group(visible=False) as python_group_5plus: |
| with gr.Tabs(): |
| with gr.Tab("app.py") as python_tab_5_1: |
| python_code_5_1 = gr.Code(language="python", lines=20, interactive=True, label="app.py") |
| with gr.Tab("file 2") as python_tab_5_2: |
| python_code_5_2 = gr.Code(language="python", lines=18, interactive=True, label="file 2") |
| with gr.Tab("file 3") as python_tab_5_3: |
| python_code_5_3 = gr.Code(language="python", lines=18, interactive=True, label="file 3") |
| with gr.Tab("file 4") as python_tab_5_4: |
| python_code_5_4 = gr.Code(language="python", lines=18, interactive=True, label="file 4") |
| with gr.Tab("file 5") as python_tab_5_5: |
| python_code_5_5 = gr.Code(language="python", lines=18, interactive=True, label="file 5") |
| |
| |
| with gr.Group(visible=False) as static_group_2: |
| with gr.Tabs(): |
| with gr.Tab("index.html") as static_tab_2_1: |
| static_code_2_1 = gr.Code(language="html", lines=20, interactive=True, label="index.html") |
| with gr.Tab("file 2") as static_tab_2_2: |
| static_code_2_2 = gr.Code(language="html", lines=18, interactive=True, label="file 2") |
| |
| with gr.Group(visible=False) as static_group_3: |
| with gr.Tabs(): |
| with gr.Tab("index.html") as static_tab_3_1: |
| static_code_3_1 = gr.Code(language="html", lines=20, interactive=True, label="index.html") |
| with gr.Tab("file 2") as static_tab_3_2: |
| static_code_3_2 = gr.Code(language="html", lines=18, interactive=True, label="file 2") |
| with gr.Tab("file 3") as static_tab_3_3: |
| static_code_3_3 = gr.Code(language="html", lines=18, interactive=True, label="file 3") |
| |
| with gr.Group(visible=False) as static_group_4: |
| with gr.Tabs(): |
| with gr.Tab("index.html") as static_tab_4_1: |
| static_code_4_1 = gr.Code(language="html", lines=20, interactive=True, label="index.html") |
| with gr.Tab("file 2") as static_tab_4_2: |
| static_code_4_2 = gr.Code(language="html", lines=18, interactive=True, label="file 2") |
| with gr.Tab("file 3") as static_tab_4_3: |
| static_code_4_3 = gr.Code(language="html", lines=18, interactive=True, label="file 3") |
| with gr.Tab("file 4") as static_tab_4_4: |
| static_code_4_4 = gr.Code(language="html", lines=18, interactive=True, label="file 4") |
| |
| with gr.Group(visible=False) as static_group_5plus: |
| with gr.Tabs(): |
| with gr.Tab("index.html") as static_tab_5_1: |
| static_code_5_1 = gr.Code(language="html", lines=20, interactive=True, label="index.html") |
| with gr.Tab("file 2") as static_tab_5_2: |
| static_code_5_2 = gr.Code(language="html", lines=18, interactive=True, label="file 2") |
| with gr.Tab("file 3") as static_tab_5_3: |
| static_code_5_3 = gr.Code(language="html", lines=18, interactive=True, label="file 3") |
| with gr.Tab("file 4") as static_tab_5_4: |
| static_code_5_4 = gr.Code(language="html", lines=18, interactive=True, label="file 4") |
| with gr.Tab("file 5") as static_tab_5_5: |
| static_code_5_5 = gr.Code(language="html", lines=18, interactive=True, label="file 5") |
| |
| with gr.Group(visible=False) as react_group: |
| with gr.Tabs(): |
| with gr.Tab("Dockerfile"): |
| react_code_dockerfile = gr.Code(language="dockerfile", lines=15, interactive=True, label="Dockerfile") |
| with gr.Tab("package.json"): |
| react_code_package_json = gr.Code(language="json", lines=20, interactive=True, label="package.json") |
| with gr.Tab("next.config.js"): |
| react_code_next_config = gr.Code(language="javascript", lines=15, interactive=True, label="next.config.js") |
| with gr.Tab("postcss.config.js"): |
| react_code_postcss_config = gr.Code(language="javascript", lines=10, interactive=True, label="postcss.config.js") |
| with gr.Tab("tailwind.config.js"): |
| react_code_tailwind_config = gr.Code(language="javascript", lines=15, interactive=True, label="tailwind.config.js") |
| with gr.Tab("pages/_app.js"): |
| react_code_pages_app = gr.Code(language="javascript", lines=15, interactive=True, label="pages/_app.js") |
| with gr.Tab("pages/index.js"): |
| react_code_pages_index = gr.Code(language="javascript", lines=20, interactive=True, label="pages/index.js") |
| with gr.Tab("components/ChatApp.jsx"): |
| react_code_components = gr.Code(language="javascript", lines=25, interactive=True, label="components/ChatApp.jsx") |
| with gr.Tab("styles/globals.css"): |
| react_code_styles = gr.Code(language="css", lines=20, interactive=True, label="styles/globals.css") |
|
|
| |
| |
| |
| |
| |
| |
| history_output = gr.Chatbot(show_label=False, height=400, type="messages", visible=False) |
|
|
| |
| generating_status = gr.Markdown("", visible=False) |
|
|
| |
| def handle_import_project(url): |
| if not url.strip(): |
| return [ |
| gr.update(value="Please enter a URL.", visible=True), |
| gr.update(), |
| gr.update(), |
| [], |
| [], |
| gr.update(value="Publish", visible=False), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| [] |
| ] |
|
|
| kind, meta = _parse_repo_or_model_url(url) |
| if kind == "hf_space": |
| status, code = load_project_from_url(url) |
| |
| is_valid, username, project_name = check_hf_space_url(url) |
| space_name = f"{username}/{project_name}" if is_valid else "" |
| loaded_history = [[f"Imported Space from {url}", code]] |
| |
| |
| code_lang = "html" |
| framework_type = "html" |
| |
| |
| if is_streamlit_code(code): |
| code_lang = "python" |
| framework_type = "streamlit" |
| elif is_gradio_code(code): |
| code_lang = "python" |
| framework_type = "gradio" |
| elif "=== index.html ===" in code and "=== index.js ===" in code and "=== style.css ===" in code: |
| |
| code_lang = "html" |
| framework_type = "transformers.js" |
| elif ("import " in code or "def " in code) and not ("<!DOCTYPE html>" in code or "<html" in code): |
| |
| |
| code_lang = "python" |
| framework_type = "gradio" |
| |
| |
| return [ |
| gr.update(value=status, visible=True), |
| gr.update(value=code, language=code_lang), |
| gr.update(value="", visible=False), |
| loaded_history, |
| history_to_chatbot_messages(loaded_history), |
| gr.update(value="Publish", visible=True), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(value=framework_type), |
| history_to_chatbot_messages(loaded_history) |
| ] |
| else: |
| |
| status, code, _ = import_repo_to_app(url) |
| loaded_history = [[f"Imported Repo/Model from {url}", code]] |
| code_lang = "python" |
| framework_type = "gradio" |
| lower = (code or "").lower() |
| if code.strip().startswith("<!doctype html>") or code.strip().startswith("<html"): |
| code_lang = "html" |
| framework_type = "html" |
| elif "```json" in lower: |
| code_lang = "json" |
| framework_type = "json" |
| return [ |
| gr.update(value=status, visible=True), |
| gr.update(value=code, language=code_lang), |
| gr.update(value="", visible=False), |
| loaded_history, |
| history_to_chatbot_messages(loaded_history), |
| gr.update(value="Publish", visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(value=framework_type), |
| history_to_chatbot_messages(loaded_history) |
| ] |
|
|
| |
| def handle_import_repo(url, framework): |
| status, code, preview = import_repo_to_app(url, framework) |
| |
| code_lang = "python" |
| lowered = (code or "").lower() |
| if code.strip().startswith("<!doctype html>") or code.strip().startswith("<html"): |
| code_lang = "html" |
| elif "import gradio" in lowered or "from gradio" in lowered: |
| code_lang = "python" |
| elif "streamlit as st" in lowered or "import streamlit" in lowered: |
| code_lang = "python" |
| elif "from transformers" in lowered or "import transformers" in lowered: |
| code_lang = "python" |
| elif "from diffusers" in lowered or "import diffusers" in lowered: |
| code_lang = "python" |
| return [ |
| gr.update(value=status, visible=True), |
| gr.update(value=code, language=code_lang), |
| gr.update(value=""), |
| gr.update(value=f"URL: {url}\n\n{status}"), |
| ] |
|
|
| |
| def update_code_language(language): |
| return gr.update(language=get_gradio_language(language)) |
|
|
|
|
| language_dropdown.change(update_code_language, inputs=language_dropdown, outputs=code_output) |
|
|
| |
| def toggle_editors(language, code_text): |
| if language == "transformers.js": |
| files = parse_transformers_js_output(code_text or "") |
| |
| editors_visible = True if (files.get('index.html') and files.get('index.js') and files.get('style.css')) else False |
| return [ |
| gr.update(visible=not editors_visible), |
| gr.update(visible=editors_visible), |
| gr.update(value=files.get('index.html', '')), |
| gr.update(value=files.get('index.js', '')), |
| gr.update(value=files.get('style.css', '')), |
| |
| gr.update(visible=False), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| ] |
| elif language == "react": |
| files = parse_react_output(code_text or "") |
| |
| editors_visible = True if files else False |
| if editors_visible: |
| return [ |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| |
| gr.update(visible=editors_visible), |
| gr.update(value=files.get('Dockerfile', '')), |
| gr.update(value=files.get('package.json', '')), |
| gr.update(value=files.get('next.config.js', '')), |
| gr.update(value=files.get('postcss.config.js', '')), |
| gr.update(value=files.get('tailwind.config.js', '')), |
| gr.update(value=files.get('pages/_app.js', '')), |
| gr.update(value=files.get('pages/index.js', '')), |
| gr.update(value=files.get('components/ChatApp.jsx', '')), |
| gr.update(value=files.get('styles/globals.css', '')), |
| ] |
| else: |
| return [ |
| gr.update(visible=True), |
| gr.update(visible=False), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| |
| gr.update(visible=False), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| ] |
| else: |
| return [ |
| gr.update(visible=True), |
| gr.update(visible=False), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| |
| gr.update(visible=False), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| ] |
|
|
| language_dropdown.change( |
| toggle_editors, |
| inputs=[language_dropdown, code_output], |
| outputs=[code_output, tjs_group, tjs_html_code, tjs_js_code, tjs_css_code, react_group, react_code_dockerfile, react_code_package_json, react_code_next_config, react_code_postcss_config, react_code_tailwind_config, react_code_pages_app, react_code_pages_index, react_code_components, react_code_styles], |
| ) |
| |
| def toggle_python_editors(language, code_text): |
| if language not in ["gradio", "streamlit"]: |
| return [ |
| gr.update(visible=True), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| |
| gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() |
| ] |
|
|
| files = parse_multi_file_python_output(code_text or "") |
|
|
| if not isinstance(files, dict) or len(files) <= 1: |
| |
| return [ |
| gr.update(visible=True), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| |
| gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() |
| ] |
|
|
| |
| |
| ordered_paths = [] |
| main_files = ['app.py', 'streamlit_app.py', 'main.py'] |
| for main_file in main_files: |
| if main_file in files: |
| ordered_paths.append(main_file) |
| break |
| |
| for p in sorted(files.keys()): |
| if p not in ordered_paths: |
| ordered_paths.append(p) |
|
|
| num_files = len(ordered_paths) |
| |
| |
| updates = [gr.update(visible=False)] |
| |
| if num_files == 2: |
| updates.extend([ |
| gr.update(visible=True), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| ]) |
| |
| path1, path2 = ordered_paths[0], ordered_paths[1] |
| updates.extend([ |
| gr.update(label=path1), gr.update(value=files.get(path1, ''), label=path1, language="python"), |
| gr.update(label=path2), gr.update(value=files.get(path2, ''), label=path2, language="python"), |
| |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() |
| ]) |
| elif num_files == 3: |
| updates.extend([ |
| gr.update(visible=False), |
| gr.update(visible=True), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| ]) |
| |
| path1, path2, path3 = ordered_paths[0], ordered_paths[1], ordered_paths[2] |
| updates.extend([ |
| |
| gr.update(), gr.update(), gr.update(), gr.update(), |
| |
| gr.update(label=path1), gr.update(value=files.get(path1, ''), label=path1, language="python"), |
| gr.update(label=path2), gr.update(value=files.get(path2, ''), label=path2, language="python"), |
| gr.update(label=path3), gr.update(value=files.get(path3, ''), label=path3, language="python"), |
| |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() |
| ]) |
| elif num_files == 4: |
| updates.extend([ |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=True), |
| gr.update(visible=False), |
| ]) |
| |
| paths = ordered_paths[:4] |
| updates.extend([ |
| |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| |
| gr.update(label=paths[0]), gr.update(value=files.get(paths[0], ''), label=paths[0], language="python"), |
| gr.update(label=paths[1]), gr.update(value=files.get(paths[1], ''), label=paths[1], language="python"), |
| gr.update(label=paths[2]), gr.update(value=files.get(paths[2], ''), label=paths[2], language="python"), |
| gr.update(label=paths[3]), gr.update(value=files.get(paths[3], ''), label=paths[3], language="python"), |
| |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() |
| ]) |
| else: |
| updates.extend([ |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=True), |
| ]) |
| |
| paths = ordered_paths[:5] |
| updates.extend([ |
| |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| |
| gr.update(label=paths[0]), gr.update(value=files.get(paths[0], ''), label=paths[0], language="python"), |
| gr.update(label=paths[1]), gr.update(value=files.get(paths[1], ''), label=paths[1], language="python"), |
| gr.update(label=paths[2]), gr.update(value=files.get(paths[2], ''), label=paths[2], language="python"), |
| gr.update(label=paths[3]), gr.update(value=files.get(paths[3], ''), label=paths[3], language="python"), |
| gr.update(label=paths[4]), gr.update(value=files.get(paths[4], ''), label=paths[4], language="python"), |
| ]) |
|
|
| return updates |
|
|
| language_dropdown.change( |
| toggle_python_editors, |
| inputs=[language_dropdown, code_output], |
| outputs=[ |
| code_output, python_group_2, python_group_3, python_group_4, python_group_5plus, |
| python_tab_2_1, python_code_2_1, python_tab_2_2, python_code_2_2, |
| python_tab_3_1, python_code_3_1, python_tab_3_2, python_code_3_2, python_tab_3_3, python_code_3_3, |
| python_tab_4_1, python_code_4_1, python_tab_4_2, python_code_4_2, python_tab_4_3, python_code_4_3, python_tab_4_4, python_code_4_4, |
| python_tab_5_1, python_code_5_1, python_tab_5_2, python_code_5_2, python_tab_5_3, python_code_5_3, python_tab_5_4, python_code_5_4, python_tab_5_5, python_code_5_5 |
| ], |
| ) |
|
|
| |
| def toggle_static_editors(language, code_text): |
| |
| if language != "html": |
| return [ |
| gr.update(visible=True), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| |
| gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() |
| ] |
|
|
| |
| original_files = parse_multipage_html_output(code_text or "") |
| |
| |
| |
| if not isinstance(original_files, dict) or len(original_files) <= 1: |
| |
| return [ |
| gr.update(visible=True), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| |
| gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() |
| ] |
| |
| |
| files = validate_and_autofix_files(original_files) |
|
|
| |
| |
| ordered_paths = [] |
| if 'index.html' in files: |
| ordered_paths.append('index.html') |
| for p in sorted(files.keys()): |
| if p == 'index.html': |
| continue |
| ordered_paths.append(p) |
|
|
| |
| def _lang_for(path: str): |
| p = (path or '').lower() |
| if p.endswith('.html'): |
| return 'html' |
| if p.endswith('.css'): |
| return 'css' |
| if p.endswith('.js'): |
| return 'javascript' |
| if p.endswith('.json'): |
| return 'json' |
| if p.endswith('.md') or p.endswith('.markdown'): |
| return 'markdown' |
| return 'html' |
|
|
| num_files = len(ordered_paths) |
| |
| |
| |
| |
| updates = [ |
| gr.update(visible=True), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| gr.update(visible=False), |
| ] |
| |
| |
| updates.extend([ |
| |
| gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), |
| gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() |
| ]) |
| |
| return updates |
|
|
| |
| language_dropdown.change( |
| toggle_static_editors, |
| inputs=[language_dropdown, code_output], |
| outputs=[ |
| code_output, |
| static_group_2, static_group_3, static_group_4, static_group_5plus, |
| static_tab_2_1, static_code_2_1, static_tab_2_2, static_code_2_2, |
| static_tab_3_1, static_code_3_1, static_tab_3_2, static_code_3_2, static_tab_3_3, static_code_3_3, |
| static_tab_4_1, static_code_4_1, static_tab_4_2, static_code_4_2, static_tab_4_3, static_code_4_3, static_tab_4_4, static_code_4_4, |
| static_tab_5_1, static_code_5_1, static_tab_5_2, static_code_5_2, static_tab_5_3, static_code_5_3, static_tab_5_4, static_code_5_4, static_tab_5_5, static_code_5_5, |
| ], |
| ) |
|
|
| def sync_tjs_from_code(code_text, language): |
| if language != "transformers.js": |
| return [gr.update(), gr.update(), gr.update(), gr.update()] |
| files = parse_transformers_js_output(code_text or "") |
| |
| editors_visible = True if (files.get('index.html') and files.get('index.js') and files.get('style.css')) else None |
| return [ |
| gr.update(value=files.get('index.html', '')), |
| gr.update(value=files.get('index.js', '')), |
| gr.update(value=files.get('style.css', '')), |
| gr.update(visible=editors_visible) if editors_visible is not None else gr.update(), |
| ] |
|
|
| |
| code_output.change( |
| sync_tjs_from_code, |
| inputs=[code_output, language_dropdown], |
| outputs=[tjs_html_code, tjs_js_code, tjs_css_code, tjs_group], |
| ) |
|
|
| |
| |
| |
| |
| |
|
|
| |
| def show_tjs_deployment_message(*args): |
| return """ |
| <div style='padding: 1.5em; text-align: center; background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); color: white; border-radius: 10px;'> |
| <h3 style='margin-top: 0; color: white;'>🚀 Transformers.js App Ready!</h3> |
| <p style='margin: 0.5em 0; opacity: 0.9;'>Your multi-file Transformers.js application is ready for deployment.</p> |
| <p style='margin: 0.5em 0; font-weight: bold;'>👉 Use the Deploy button in the sidebar to publish your app!</p> |
| </div> |
| """ |
| |
|
|
| def show_deploy_components(*args): |
| return gr.Button(visible=True) |
|
|
| def hide_deploy_components(*args): |
| return gr.Button(visible=True) |
| |
| |
| def toggle_import_textbox(url_visible): |
| |
| |
| return gr.update(visible=True) |
| |
| load_project_btn.click( |
| fn=toggle_import_textbox, |
| inputs=[load_project_url], |
| outputs=[load_project_url] |
| ).then( |
| handle_import_project, |
| inputs=[load_project_url], |
| outputs=[ |
| load_project_status, |
| code_output, |
| load_project_url, |
| history, |
| history_output, |
| deploy_btn, |
| import_header_md, |
| load_project_btn, |
| language_dropdown, |
| chat_history, |
| ], |
| ) |
|
|
|
|
|
|
|
|
|
|
| def begin_generation_ui(agent_enabled): |
| |
| |
| if agent_enabled: |
| return [gr.update(), gr.update(visible=False)] |
| else: |
| |
| return [gr.update(open=False), gr.update(visible=False)] |
|
|
| def end_generation_ui(): |
| |
| return [gr.update(open=True), gr.update(visible=False)] |
| |
| def close_sidebar_for_coding(): |
| |
| return gr.update(open=False) |
|
|
| def generation_code_wrapper(inp, sett, hist, model, lang, prov, agent_enabled, agent_state, profile: Optional[gr.OAuthProfile] = None, token: Optional[gr.OAuthToken] = None): |
| """Wrapper to call generation_code or agent mode based on settings""" |
| |
| |
| if agent_enabled and agent_state["stage"] == "initial": |
| |
| |
| for updated_hist, chatbot_msgs in agent_generate_with_questions( |
| inp, sett, hist, model, lang, prov, profile, token |
| ): |
| |
| new_agent_state = { |
| "stage": "waiting_for_answers", |
| "original_query": inp, |
| "questions": updated_hist[-1][1] if updated_hist else "" |
| } |
| |
| yield "", updated_hist, chatbot_msgs, chatbot_msgs, new_agent_state, gr.update() |
| return |
| |
| elif agent_enabled and agent_state["stage"] == "waiting_for_answers": |
| |
| original_query = agent_state.get("original_query", "") |
| questions = agent_state.get("questions", "") |
| |
| |
| started_code_generation = False |
| |
| |
| for result in agent_process_answers_and_generate( |
| inp, original_query, questions, sett, hist, model, lang, prov, |
| profile, token, code_output, history_output, history |
| ): |
| |
| code_val = result.get(code_output, "") |
| hist_val = result.get(history, hist) |
| history_output_val = result.get(history_output, []) |
| |
| |
| reset_agent_state = { |
| "stage": "initial", |
| "original_query": "", |
| "questions": "" |
| } |
| |
| |
| if code_val and not started_code_generation: |
| sidebar_update = gr.update(open=False) |
| started_code_generation = True |
| else: |
| sidebar_update = gr.update() |
| |
| |
| yield code_val, hist_val, history_output_val, history_output_val, reset_agent_state, sidebar_update |
| return |
| |
| else: |
| |
| |
| for result in generation_code(inp, sett, hist, model, lang, prov, profile, token, code_output, history_output, history): |
| |
| |
| code_val = result.get(code_output, "") |
| hist_val = result.get(history, hist) |
| history_output_val = result.get(history_output, []) |
| |
| yield code_val, hist_val, history_output_val, history_output_val, agent_state, gr.update() |
|
|
| |
| agent_mode_checkbox.change( |
| lambda enabled: enabled, |
| inputs=[agent_mode_checkbox], |
| outputs=[agent_mode_enabled] |
| ) |
| |
| btn.click( |
| begin_generation_ui, |
| inputs=[agent_mode_enabled], |
| outputs=[sidebar, generating_status], |
| show_progress="hidden", |
| ).then( |
| generation_code_wrapper, |
| inputs=[input, setting, history, current_model, language_dropdown, provider_state, agent_mode_enabled, agent_conversation_state], |
| outputs=[code_output, history, history_output, chat_history, agent_conversation_state, sidebar] |
| ).then( |
| end_generation_ui, |
| inputs=None, |
| outputs=[sidebar, generating_status] |
| ).then( |
| |
| toggle_editors, |
| inputs=[language_dropdown, code_output], |
| outputs=[code_output, tjs_group, tjs_html_code, tjs_js_code, tjs_css_code, react_group, react_code_dockerfile, react_code_package_json, react_code_next_config, react_code_postcss_config, react_code_tailwind_config, react_code_pages_app, react_code_pages_index, react_code_components, react_code_styles] |
| ).then( |
| |
| toggle_static_editors, |
| inputs=[language_dropdown, code_output], |
| outputs=[ |
| code_output, |
| static_group_2, static_group_3, static_group_4, static_group_5plus, |
| static_tab_2_1, static_code_2_1, static_tab_2_2, static_code_2_2, |
| static_tab_3_1, static_code_3_1, static_tab_3_2, static_code_3_2, static_tab_3_3, static_code_3_3, |
| static_tab_4_1, static_code_4_1, static_tab_4_2, static_code_4_2, static_tab_4_3, static_code_4_3, static_tab_4_4, static_code_4_4, |
| static_tab_5_1, static_code_5_1, static_tab_5_2, static_code_5_2, static_tab_5_3, static_code_5_3, static_tab_5_4, static_code_5_4, static_tab_5_5, static_code_5_5, |
| ] |
| ).then( |
| |
| toggle_python_editors, |
| inputs=[language_dropdown, code_output], |
| outputs=[ |
| code_output, python_group_2, python_group_3, python_group_4, python_group_5plus, |
| python_tab_2_1, python_code_2_1, python_tab_2_2, python_code_2_2, |
| python_tab_3_1, python_code_3_1, python_tab_3_2, python_code_3_2, python_tab_3_3, python_code_3_3, |
| python_tab_4_1, python_code_4_1, python_tab_4_2, python_code_4_2, python_tab_4_3, python_code_4_3, python_tab_4_4, python_code_4_4, |
| python_tab_5_1, python_code_5_1, python_tab_5_2, python_code_5_2, python_tab_5_3, python_code_5_3, python_tab_5_4, python_code_5_4, python_tab_5_5, python_code_5_5 |
| ] |
| ).then( |
| show_deploy_components, |
| None, |
| [deploy_btn] |
| ) |
|
|
| |
| input.submit( |
| begin_generation_ui, |
| inputs=[agent_mode_enabled], |
| outputs=[sidebar, generating_status], |
| show_progress="hidden", |
| ).then( |
| generation_code_wrapper, |
| inputs=[input, setting, history, current_model, language_dropdown, provider_state, agent_mode_enabled, agent_conversation_state], |
| outputs=[code_output, history, history_output, chat_history, agent_conversation_state, sidebar] |
| ).then( |
| end_generation_ui, |
| inputs=None, |
| outputs=[sidebar, generating_status] |
| ).then( |
| |
| toggle_editors, |
| inputs=[language_dropdown, code_output], |
| outputs=[code_output, tjs_group, tjs_html_code, tjs_js_code, tjs_css_code, react_group, react_code_dockerfile, react_code_package_json, react_code_next_config, react_code_postcss_config, react_code_tailwind_config, react_code_pages_app, react_code_pages_index, react_code_components, react_code_styles] |
| ).then( |
| |
| toggle_static_editors, |
| inputs=[language_dropdown, code_output], |
| outputs=[ |
| code_output, |
| static_group_2, static_group_3, static_group_4, static_group_5plus, |
| static_tab_2_1, static_code_2_1, static_tab_2_2, static_code_2_2, |
| static_tab_3_1, static_code_3_1, static_tab_3_2, static_code_3_2, static_tab_3_3, static_code_3_3, |
| static_tab_4_1, static_code_4_1, static_tab_4_2, static_code_4_2, static_tab_4_3, static_code_4_3, static_tab_4_4, static_code_4_4, |
| static_tab_5_1, static_code_5_1, static_tab_5_2, static_code_5_2, static_tab_5_3, static_code_5_3, static_tab_5_4, static_code_5_4, static_tab_5_5, static_code_5_5, |
| ] |
| ).then( |
| |
| toggle_python_editors, |
| inputs=[language_dropdown, code_output], |
| outputs=[ |
| code_output, python_group_2, python_group_3, python_group_4, python_group_5plus, |
| python_tab_2_1, python_code_2_1, python_tab_2_2, python_code_2_2, |
| python_tab_3_1, python_code_3_1, python_tab_3_2, python_code_3_2, python_tab_3_3, python_code_3_3, |
| python_tab_4_1, python_code_4_1, python_tab_4_2, python_code_4_2, python_tab_4_3, python_code_4_3, python_tab_4_4, python_code_4_4, |
| python_tab_5_1, python_code_5_1, python_tab_5_2, python_code_5_2, python_tab_5_3, python_code_5_3, python_tab_5_4, python_code_5_4, python_tab_5_5, python_code_5_5 |
| ] |
| ).then( |
| show_deploy_components, |
| None, |
| [deploy_btn] |
| ) |
|
|
| |
| def _find_model_by_name(name: str): |
| for m in AVAILABLE_MODELS: |
| if m["name"].lower() == name.lower(): |
| return m |
| return None |
|
|
| def _extract_url(text: str) -> Optional[str]: |
| import re |
| match = re.search(r"https?://[^\s]+", text or "") |
| return match.group(0) if match else None |
|
|
|
|
| |
| def show_deployment_message(code, language, *args): |
| if not code or not code.strip(): |
| return "<div style='padding:1em;color:#888;text-align:center;'>Generate some code to see deployment options.</div>" |
| return f""" |
| <div style='padding: 1.5em; text-align: center; background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); color: white; border-radius: 10px;'> |
| <h3 style='margin-top: 0; color: white;'>Ready to Deploy!</h3> |
| <p style='margin: 0.5em 0; opacity: 0.9;'>Your {language.upper()} code is ready for deployment.</p> |
| <p style='margin: 0.5em 0; font-weight: bold;'>👉 Use the Deploy button in the sidebar to publish your app!</p> |
| </div> |
| """ |
| |
| def reset_agent_state(): |
| """Reset agent conversation state when clearing history""" |
| return { |
| "stage": "initial", |
| "original_query": "", |
| "questions": "" |
| } |
| |
| clear_btn.click(clear_history, outputs=[history, history_output, chat_history]) |
| clear_btn.click(hide_deploy_components, None, [deploy_btn]) |
| clear_btn.click(reset_agent_state, outputs=[agent_conversation_state]) |
| |
| clear_btn.click( |
| lambda: gr.update(value="Publish"), |
| outputs=[deploy_btn] |
| ) |
|
|
| |
| |
| def generate_random_app_name(): |
| """Generate a random app name that's unlikely to clash with existing apps""" |
| import random |
| import string |
| |
| |
| prefixes = ["my", "cool", "awesome", "smart", "quick", "super", "mini", "auto", "fast", "easy"] |
| |
| suffixes = ["app", "tool", "hub", "space", "demo", "ai", "gen", "bot", "lab", "studio"] |
| |
| adjectives = ["blue", "red", "green", "bright", "dark", "light", "swift", "bold", "clean", "fresh"] |
| |
| |
| patterns = [ |
| lambda: f"{random.choice(prefixes)}-{random.choice(suffixes)}-{random.randint(100, 999)}", |
| lambda: f"{random.choice(adjectives)}-{random.choice(suffixes)}-{random.randint(10, 99)}", |
| lambda: f"{random.choice(prefixes)}-{random.choice(adjectives)}-{random.choice(suffixes)}", |
| lambda: f"app-{''.join(random.choices(string.ascii_lowercase, k=6))}-{random.randint(10, 99)}", |
| lambda: f"{random.choice(suffixes)}-{''.join(random.choices(string.ascii_lowercase + string.digits, k=8))}" |
| ] |
| |
| return random.choice(patterns)() |
|
|
| def deploy_with_history_tracking( |
| code, |
| language, |
| history, |
| profile: Optional[gr.OAuthProfile] = None, |
| token: Optional[gr.OAuthToken] = None |
| ): |
| """Wrapper function that handles history tracking for deployments""" |
| |
| username = profile.username if profile else None |
| existing_space = None |
| |
| |
| if history and username: |
| for user_msg, assistant_msg in history: |
| if assistant_msg and "✅ Deployed!" in assistant_msg: |
| import re |
| |
| match = re.search(r'huggingface\.co/spaces/([^/\s\)]+/[^/\s\)]+)', assistant_msg) |
| if match: |
| existing_space = match.group(1) |
| break |
| elif assistant_msg and "✅ Updated!" in assistant_msg: |
| import re |
| |
| match = re.search(r'huggingface\.co/spaces/([^/\s\)]+/[^/\s\)]+)', assistant_msg) |
| if match: |
| existing_space = match.group(1) |
| break |
| elif user_msg and user_msg.startswith("Imported Space from"): |
| import re |
| |
| match = re.search(r'huggingface\.co/spaces/([^/\s\)]+/[^/\s\)]+)', user_msg) |
| if match: |
| imported_space = match.group(1) |
| |
| if imported_space.startswith(f"{username}/"): |
| existing_space = imported_space |
| break |
| |
| |
| |
| |
| status = deploy_to_user_space_original(code, language, existing_space, profile, token) |
| |
| |
| updated_history = history |
| if isinstance(status, dict) and "value" in status and "✅" in status["value"]: |
| action_type = "Deploy" if "Deployed!" in status["value"] else "Update" |
| if existing_space: |
| updated_history = history + [[f"{action_type} {language} app to {existing_space}", status["value"]]] |
| else: |
| updated_history = history + [[f"{action_type} {language} app", status["value"]]] |
| |
| return [status, updated_history] |
|
|
| def deploy_to_user_space_original( |
| code, |
| language, |
| existing_space_name=None, |
| profile: Optional[gr.OAuthProfile] = None, |
| token: Optional[gr.OAuthToken] = None |
| ): |
| import shutil |
| if not code or not code.strip(): |
| return gr.update(value="No code to deploy.", visible=True) |
| if profile is None or token is None: |
| return gr.update(value="Please log in with your Hugging Face account to deploy to your own Space. Otherwise, use the default deploy (opens in new tab).", visible=True) |
| |
| |
| if not token.token or token.token == "hf_": |
| return gr.update(value="Error: Invalid token. Please log in again with your Hugging Face account to get a valid write token.", visible=True) |
| |
| |
| username = profile.username |
| if existing_space_name and existing_space_name.startswith(f"{username}/"): |
| |
| repo_id = existing_space_name |
| space_name = existing_space_name.split('/')[-1] |
| is_update = True |
| else: |
| |
| space_name = generate_random_app_name() |
| repo_id = f"{username}/{space_name}" |
| is_update = False |
| |
| language_to_sdk_map = { |
| "gradio": "gradio", |
| "streamlit": "docker", |
| "react": "docker", |
| "html": "static", |
| "transformers.js": "static", |
| "comfyui": "static" |
| } |
| sdk = language_to_sdk_map.get(language, "gradio") |
| |
| |
| api = HfApi(token=token.token) |
| |
| if not is_update and sdk != "docker" and language not in ["transformers.js"]: |
| try: |
| api.create_repo( |
| repo_id=repo_id, |
| repo_type="space", |
| space_sdk=sdk, |
| exist_ok=True |
| ) |
| except Exception as e: |
| return gr.update(value=f"Error creating Space: {e}", visible=True) |
| |
| if sdk == "docker" and language in ["streamlit", "react"]: |
| try: |
| |
| if not is_update: |
| |
| from huggingface_hub import create_repo |
| |
| if language == "react": |
| |
| created_repo = create_repo( |
| repo_id=repo_id, |
| repo_type="space", |
| space_sdk="docker", |
| token=token.token, |
| exist_ok=True |
| ) |
| else: |
| |
| created_repo = create_repo( |
| repo_id=repo_id, |
| repo_type="space", |
| space_sdk="docker", |
| token=token.token, |
| exist_ok=True |
| ) |
| |
| |
| if language == "react": |
| |
| files = parse_react_output(code) |
| if not files: |
| return gr.update(value="Error: Could not parse React output. Please regenerate the code.", visible=True) |
| |
| |
| if 'Dockerfile' not in files: |
| files['Dockerfile'] = """FROM node:18-slim |
| |
| # Use the existing node user (UID 1000) |
| USER node |
| |
| # Set environment variables |
| ENV HOME=/home/node \\ |
| PATH=/home/node/.local/bin:$PATH |
| |
| # Set working directory |
| WORKDIR /home/node/app |
| |
| # Copy package files with proper ownership |
| COPY --chown=node:node package*.json ./ |
| |
| # Install dependencies |
| RUN npm install |
| |
| # Copy rest of the application with proper ownership |
| COPY --chown=node:node . . |
| |
| # Build the Next.js app |
| RUN npm run build |
| |
| # Expose port 7860 |
| EXPOSE 7860 |
| |
| # Start the application on port 7860 |
| CMD ["npm", "start", "--", "-p", "7860"] |
| """ |
| |
| |
| import tempfile |
| import time |
| |
| for file_name, file_content in files.items(): |
| if not file_content: |
| continue |
| |
| success = False |
| last_error = None |
| max_attempts = 3 |
| |
| for attempt in range(max_attempts): |
| try: |
| |
| if file_name == 'Dockerfile': |
| suffix = '' |
| else: |
| suffix = f".{file_name.split('.')[-1]}" |
| |
| with tempfile.NamedTemporaryFile("w", suffix=suffix, delete=False) as f: |
| f.write(file_content) |
| temp_path = f.name |
| |
| api.upload_file( |
| path_or_fileobj=temp_path, |
| path_in_repo=file_name, |
| repo_id=repo_id, |
| repo_type="space" |
| ) |
| success = True |
| break |
| |
| except Exception as e: |
| last_error = e |
| error_msg = str(e) |
| if "403 Forbidden" in error_msg and "write token" in error_msg: |
| return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True) |
| |
| if attempt < max_attempts - 1: |
| time.sleep(2) |
| finally: |
| import os |
| if 'temp_path' in locals(): |
| os.unlink(temp_path) |
| |
| if not success: |
| return gr.update(value=f"Error uploading {file_name}: {last_error}", visible=True) |
| |
| |
| add_anycoder_tag_to_readme(api, repo_id, app_port=7860) |
| |
| space_url = f"https://huggingface.co/spaces/{repo_id}" |
| action_text = "Updated" if is_update else "Deployed" |
| return gr.update(value=f"✅ {action_text}! [Open your React Space here]({space_url})", visible=True) |
| |
| |
| files = parse_multi_file_python_output(code) |
| if not files: |
| return gr.update(value="Error: Could not parse Streamlit output. Please regenerate the code.", visible=True) |
| |
| |
| has_streamlit_app = 'streamlit_app.py' in files or 'app.py' in files |
| has_requirements = 'requirements.txt' in files |
| has_dockerfile = 'Dockerfile' in files |
| |
| if not has_streamlit_app: |
| return gr.update(value="Error: Missing streamlit_app.py. Please regenerate the code.", visible=True) |
| |
| |
| if not has_dockerfile: |
| |
| files['Dockerfile'] = """FROM python:3.11-slim |
| |
| # Set up user with ID 1000 |
| RUN useradd -m -u 1000 user |
| USER user |
| ENV HOME=/home/user \\ |
| PATH=/home/user/.local/bin:$PATH |
| |
| # Set working directory |
| WORKDIR $HOME/app |
| |
| # Copy requirements file with proper ownership |
| COPY --chown=user requirements.txt . |
| |
| # Install dependencies |
| RUN pip install --no-cache-dir -r requirements.txt |
| |
| # Copy application files with proper ownership |
| COPY --chown=user . . |
| |
| # Expose port 7860 |
| EXPOSE 7860 |
| |
| # Start Streamlit app |
| CMD ["streamlit", "run", "streamlit_app.py", "--server.port=7860", "--server.address=0.0.0.0"] |
| """ |
| |
| if not has_requirements: |
| |
| main_app = files.get('streamlit_app.py') or files.get('app.py', '') |
| import_statements = extract_import_statements(main_app) |
| files['requirements.txt'] = generate_requirements_txt_with_llm(import_statements) |
| |
| |
| import tempfile |
| import time |
| |
| for file_name, file_content in files.items(): |
| if not file_content: |
| continue |
| |
| success = False |
| last_error = None |
| max_attempts = 3 |
| |
| for attempt in range(max_attempts): |
| try: |
| |
| if file_name == 'Dockerfile': |
| suffix = '' |
| else: |
| suffix = f".{file_name.split('.')[-1]}" |
| |
| with tempfile.NamedTemporaryFile("w", suffix=suffix, delete=False) as f: |
| f.write(file_content) |
| temp_path = f.name |
| |
| api.upload_file( |
| path_or_fileobj=temp_path, |
| path_in_repo=file_name, |
| repo_id=repo_id, |
| repo_type="space" |
| ) |
| success = True |
| break |
| |
| except Exception as e: |
| last_error = e |
| error_msg = str(e) |
| if "403 Forbidden" in error_msg and "write token" in error_msg: |
| return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True) |
| |
| if attempt < max_attempts - 1: |
| time.sleep(2) |
| finally: |
| import os |
| if 'temp_path' in locals(): |
| os.unlink(temp_path) |
| |
| if not success: |
| return gr.update(value=f"Error uploading {file_name}: {last_error}", visible=True) |
| |
| |
| add_anycoder_tag_to_readme(api, repo_id, app_port=7860) |
| |
| space_url = f"https://huggingface.co/spaces/{repo_id}" |
| action_text = "Updated" if is_update else "Deployed" |
| return gr.update(value=f"✅ {action_text}! [Open your Streamlit Space here]({space_url})", visible=True) |
| |
| except Exception as e: |
| error_prefix = "Error duplicating Streamlit space" if not is_update else "Error updating Streamlit space" |
| return gr.update(value=f"{error_prefix}: {e}", visible=True) |
| |
| elif language == "transformers.js": |
| try: |
| |
| if not is_update: |
| |
| from huggingface_hub import duplicate_space |
| |
| |
| duplicated_repo = duplicate_space( |
| from_id="static-templates/transformers.js", |
| to_id=space_name.strip(), |
| token=token.token, |
| exist_ok=True |
| ) |
| print("Duplicated repo result:", duplicated_repo, type(duplicated_repo)) |
| else: |
| |
| try: |
| space_info = api.space_info(repo_id) |
| if not space_info: |
| return gr.update(value=f"Error: Could not access space {repo_id} for update.", visible=True) |
| except Exception as e: |
| return gr.update(value=f"Error: Cannot update space {repo_id}. {str(e)}", visible=True) |
| |
| files = parse_transformers_js_output(code) |
| |
| if not files['index.html'] or not files['index.js'] or not files['style.css']: |
| return gr.update(value="Error: Could not parse transformers.js output. Please regenerate the code.", visible=True) |
| |
| |
| import tempfile |
| import time |
| |
| |
| files_to_upload = [ |
| ("index.html", files['index.html']), |
| ("index.js", files['index.js']), |
| ("style.css", files['style.css']) |
| ] |
| |
| |
| max_attempts = 3 |
| for file_name, file_content in files_to_upload: |
| success = False |
| last_error = None |
| |
| for attempt in range(max_attempts): |
| try: |
| with tempfile.NamedTemporaryFile("w", suffix=f".{file_name.split('.')[-1]}", delete=False) as f: |
| f.write(file_content) |
| temp_path = f.name |
| |
| api.upload_file( |
| path_or_fileobj=temp_path, |
| path_in_repo=file_name, |
| repo_id=repo_id, |
| repo_type="space" |
| ) |
| success = True |
| break |
| |
| except Exception as e: |
| last_error = e |
| error_msg = str(e) |
| if "403 Forbidden" in error_msg and "write token" in error_msg: |
| |
| return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True) |
| |
| if attempt < max_attempts - 1: |
| time.sleep(2) |
| finally: |
| import os |
| if 'temp_path' in locals(): |
| os.unlink(temp_path) |
| |
| if not success: |
| return gr.update(value=f"Error uploading {file_name}: {last_error}", visible=True) |
| |
| |
| add_anycoder_tag_to_readme(api, repo_id) |
| |
| |
| if is_update: |
| try: |
| api.restart_space(repo_id=repo_id) |
| except Exception as restart_error: |
| |
| print(f"Note: Could not restart space after update: {restart_error}") |
| |
| space_url = f"https://huggingface.co/spaces/{repo_id}" |
| action_text = "Updated" if is_update else "Deployed" |
| return gr.update(value=f"✅ {action_text}! [Open your Transformers.js Space here]({space_url})", visible=True) |
| |
| except Exception as e: |
| |
| error_msg = str(e) |
| if "'url'" in error_msg or "RepoUrl" in error_msg: |
| |
| try: |
| |
| space_url = f"https://huggingface.co/spaces/{repo_id}" |
| test_api = HfApi(token=token.token) |
| space_exists = test_api.space_info(repo_id) |
| |
| if space_exists and not is_update: |
| |
| return gr.update(value=f"✅ Deployed! Space was created successfully despite a technical error. [Open your Transformers.js Space here]({space_url})", visible=True) |
| elif space_exists and is_update: |
| |
| return gr.update(value=f"✅ Updated! Space was updated successfully despite a technical error. [Open your Transformers.js Space here]({space_url})", visible=True) |
| else: |
| |
| return gr.update(value=f"Error: Could not create/update space. Please try again manually at https://huggingface.co/new-space", visible=True) |
| except: |
| |
| repo_url = f"https://huggingface.co/spaces/{repo_id}" |
| return gr.update(value=f"Error: Could not properly handle space creation response. Space may have been created successfully. Check: {repo_url}", visible=True) |
| |
| |
| action_verb = "updating" if is_update else "duplicating" |
| return gr.update(value=f"Error {action_verb} Transformers.js space: {error_msg}", visible=True) |
| |
| if sdk == "static": |
| import time |
| |
| |
| add_anycoder_tag_to_readme(api, repo_id) |
| |
| |
| files = {} |
| parse_error = None |
| try: |
| files = parse_multipage_html_output(code) |
| print(f"[Deploy] Parsed files: {list(files.keys())}") |
| files = validate_and_autofix_files(files) |
| print(f"[Deploy] After validation: {list(files.keys())}") |
| except Exception as e: |
| parse_error = str(e) |
| print(f"[Deploy] Parse error: {parse_error}") |
| files = {} |
| |
| |
| if isinstance(files, dict) and len(files) > 0 and files.get('index.html'): |
| import tempfile |
| import os |
| |
| try: |
| with tempfile.TemporaryDirectory() as tmpdir: |
| |
| for rel_path, content in files.items(): |
| safe_rel_path = rel_path.strip().lstrip('/') |
| abs_path = os.path.join(tmpdir, safe_rel_path) |
| os.makedirs(os.path.dirname(abs_path), exist_ok=True) |
| with open(abs_path, 'w') as fh: |
| fh.write(content) |
| |
| |
| api.upload_folder( |
| folder_path=tmpdir, |
| repo_id=repo_id, |
| repo_type="space" |
| ) |
| space_url = f"https://huggingface.co/spaces/{repo_id}" |
| action_text = "Updated" if is_update else "Deployed" |
| return gr.update(value=f"{action_text}! [Open your app here]({space_url})", visible=True) |
| except Exception as e: |
| error_msg = str(e) |
| if "403 Forbidden" in error_msg and "write token" in error_msg: |
| return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True) |
| else: |
| return gr.update(value=f"Error uploading static app folder: {e}", visible=True) |
| |
| |
| file_name = "index.html" |
| |
| |
| if language == "comfyui": |
| print("[Deploy] Converting ComfyUI JSON to prettified HTML display") |
| code = prettify_comfyui_json_for_html(code) |
| |
| max_attempts = 3 |
| for attempt in range(max_attempts): |
| import tempfile |
| with tempfile.NamedTemporaryFile("w", suffix=".html", delete=False) as f: |
| f.write(code) |
| temp_path = f.name |
| try: |
| api.upload_file( |
| path_or_fileobj=temp_path, |
| path_in_repo=file_name, |
| repo_id=repo_id, |
| repo_type="space" |
| ) |
| space_url = f"https://huggingface.co/spaces/{repo_id}" |
| action_text = "Updated" if is_update else "Deployed" |
| return gr.update(value=f"{action_text}! [Open your app here]({space_url})", visible=True) |
| except Exception as e: |
| error_msg = str(e) |
| if "403 Forbidden" in error_msg and "write token" in error_msg: |
| return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True) |
| elif attempt < max_attempts - 1: |
| time.sleep(2) |
| else: |
| return gr.update(value=f"Error uploading file after {max_attempts} attempts: {e}. Please check your permissions and try again.", visible=True) |
| finally: |
| import os |
| os.unlink(temp_path) |
| else: |
| |
| import_statements = extract_import_statements(code) |
| requirements_content = generate_requirements_txt_with_llm(import_statements) |
| |
| import tempfile |
| |
| |
| should_upload_requirements = True |
| if is_update: |
| try: |
| |
| existing_requirements = api.hf_hub_download( |
| repo_id=repo_id, |
| filename="requirements.txt", |
| repo_type="space" |
| ) |
| with open(existing_requirements, 'r') as f: |
| existing_content = f.read().strip() |
| |
| |
| if existing_content == requirements_content.strip(): |
| should_upload_requirements = False |
| |
| except Exception: |
| |
| should_upload_requirements = True |
| |
| |
| |
| |
| |
| add_anycoder_tag_to_readme(api, repo_id) |
| |
| |
| if ('=== app.py ===' in code or '=== requirements.txt ===' in code): |
| |
| files = parse_multi_file_python_output(code) |
| if files: |
| |
| if 'app.py' in files and 'requirements.txt' not in files: |
| import_statements = extract_import_statements(files['app.py']) |
| requirements_content = generate_requirements_txt_with_llm(import_statements) |
| files['requirements.txt'] = requirements_content |
| try: |
| from huggingface_hub import CommitOperationAdd |
| operations = [] |
| temp_files = [] |
| |
| |
| for filename, content in files.items(): |
| |
| cleaned_content = content |
| if filename.endswith('.txt') or filename.endswith('.py'): |
| |
| lines = cleaned_content.split('\n') |
| clean_lines = [] |
| for line in lines: |
| stripped = line.strip() |
| |
| if stripped == '```' or (stripped.startswith('```') and len(stripped) <= 10): |
| continue |
| clean_lines.append(line) |
| cleaned_content = '\n'.join(clean_lines) |
| |
| |
| with tempfile.NamedTemporaryFile("w", suffix=f".{filename.split('.')[-1]}", delete=False) as f: |
| f.write(cleaned_content) |
| temp_path = f.name |
| temp_files.append(temp_path) |
| |
| |
| operations.append(CommitOperationAdd( |
| path_in_repo=filename, |
| path_or_fileobj=temp_path |
| )) |
| |
| |
| api.create_commit( |
| repo_id=repo_id, |
| operations=operations, |
| commit_message=f"{'Update' if is_update else 'Deploy'} Gradio app with multiple files", |
| repo_type="space" |
| ) |
| |
| |
| for temp_path in temp_files: |
| try: |
| os.unlink(temp_path) |
| except Exception: |
| pass |
| |
| space_url = f"https://huggingface.co/spaces/{repo_id}" |
| action_text = "Updated" if is_update else "Deployed" |
| return gr.update(value=f"✅ {action_text}! [Open your Space here]({space_url})", visible=True) |
| |
| except Exception as e: |
| |
| for temp_path in temp_files: |
| try: |
| os.unlink(temp_path) |
| except Exception: |
| pass |
| |
| error_msg = str(e) |
| if "403 Forbidden" in error_msg and "write token" in error_msg: |
| return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True) |
| else: |
| return gr.update(value=f"Error uploading multi-file app: {e}", visible=True) |
| else: |
| |
| pass |
| |
| |
| file_name = "app.py" |
| with tempfile.NamedTemporaryFile("w", suffix=f".{file_name.split('.')[-1]}", delete=False) as f: |
| f.write(code) |
| temp_path = f.name |
| try: |
| api.upload_file( |
| path_or_fileobj=temp_path, |
| path_in_repo=file_name, |
| repo_id=repo_id, |
| repo_type="space" |
| ) |
| space_url = f"https://huggingface.co/spaces/{repo_id}" |
| action_text = "Updated" if is_update else "Deployed" |
| return gr.update(value=f"✅ {action_text}! [Open your Space here]({space_url})", visible=True) |
| except Exception as e: |
| error_msg = str(e) |
| if "403 Forbidden" in error_msg and "write token" in error_msg: |
| return gr.update(value=f"Error: Permission denied. Please ensure you have write access to {repo_id} and your token has the correct permissions.", visible=True) |
| else: |
| return gr.update(value=f"Error uploading file: {e}", visible=True) |
| finally: |
| import os |
| os.unlink(temp_path) |
|
|
| |
| def gather_code_for_deploy(code_text, language, html_part, js_part, css_part): |
| |
| if language == "transformers.js": |
| |
| files = { |
| 'index.html': html_part or '', |
| 'index.js': js_part or '', |
| 'style.css': css_part or '', |
| } |
| if files['index.html'] and files['index.js'] and files['style.css']: |
| return format_transformers_js_output(files) |
| return code_text |
| deploy_btn.click( |
| gather_code_for_deploy, |
| inputs=[code_output, language_dropdown, tjs_html_code, tjs_js_code, tjs_css_code], |
| outputs=[code_output], |
| queue=False, |
| ).then( |
| deploy_with_history_tracking, |
| inputs=[code_output, language_dropdown, history], |
| outputs=[deploy_status, history] |
| ).then( |
| lambda hist: history_to_chatbot_messages(hist), |
| inputs=[history], |
| outputs=[chat_history] |
| ) |
| |
| |
| |
| |
| |
| def handle_auth_update(profile: Optional[gr.OAuthProfile] = None, token: Optional[gr.OAuthToken] = None): |
| return update_ui_for_auth_status(profile, token) |
| |
| |
| login_button.click( |
| handle_auth_update, |
| inputs=[], |
| outputs=[input, btn], |
| queue=False |
| ) |
| |
| |
| demo.load( |
| handle_auth_update, |
| inputs=[], |
| outputs=[input, btn], |
| queue=False |
| ) |
| |
| |
| def load_trending_models(): |
| """Load trending models from HuggingFace Hub""" |
| models = get_trending_models(limit=10) |
| |
| choices = [(display, model_id) for display, model_id in models] |
| |
| default_value = models[0][1] if models and len(models) > 0 and models[0][1] != "" else None |
| return gr.update(choices=choices, value=default_value) |
| |
| demo.load( |
| load_trending_models, |
| inputs=[], |
| outputs=[trending_models_dropdown], |
| queue=False |
| ) |
| |
| |
| def load_trending_spaces(): |
| """Load trending spaces from HuggingFace Hub""" |
| spaces = get_trending_spaces(limit=10) |
| |
| choices = [(display, space_id) for display, space_id in spaces] |
| |
| default_value = spaces[0][1] if spaces and len(spaces) > 0 and spaces[0][1] != "" else None |
| return gr.update(choices=choices, value=default_value) |
| |
| demo.load( |
| load_trending_spaces, |
| inputs=[], |
| outputs=[trending_spaces_dropdown], |
| queue=False |
| ) |
| |
| |
| def handle_trending_model_selection(model_id, hist, is_first): |
| """Handle when user selects a trending model""" |
| |
| if is_first: |
| return [ |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| hist, |
| history_to_chatbot_messages(hist), |
| history_to_chatbot_messages(hist), |
| False, |
| gr.update(visible=False), |
| "" |
| ] |
| |
| if not model_id or model_id == "": |
| return [ |
| gr.update(value="Please select a model.", visible=True), |
| gr.update(), |
| gr.update(), |
| hist, |
| history_to_chatbot_messages(hist), |
| history_to_chatbot_messages(hist), |
| False, |
| gr.update(visible=False), |
| "" |
| ] |
| |
| |
| status, code, language, model_url = import_model_from_hf(model_id) |
| |
| |
| loaded_history = hist + [[f"Imported model: {model_id}", code]] |
| |
| |
| code_lang = "python" |
| |
| |
| show_switch_btn = "Found multiple code options" in status |
| |
| return [ |
| gr.update(value=status, visible=True), |
| gr.update(value=code, language=code_lang), |
| gr.update(value=language), |
| loaded_history, |
| history_to_chatbot_messages(loaded_history), |
| history_to_chatbot_messages(loaded_history), |
| False, |
| gr.update(visible=show_switch_btn), |
| model_id |
| ] |
| |
| trending_models_dropdown.change( |
| handle_trending_model_selection, |
| inputs=[trending_models_dropdown, history, models_first_change], |
| outputs=[ |
| trending_models_status, |
| code_output, |
| language_dropdown, |
| history, |
| history_output, |
| chat_history, |
| models_first_change, |
| switch_model_code_btn, |
| current_trending_model_id |
| ] |
| ) |
| |
| |
| def handle_switch_model_code(model_id, current_code, hist): |
| """Switch between inference provider and local transformers/diffusers code""" |
| if not model_id: |
| return [ |
| gr.update(), |
| gr.update(), |
| hist, |
| history_to_chatbot_messages(hist), |
| history_to_chatbot_messages(hist) |
| ] |
| |
| status_msg, new_code = switch_model_code_type(model_id, current_code) |
| |
| |
| switch_history = hist + [[f"Switched code type for {model_id}", new_code]] |
| |
| return [ |
| gr.update(value=status_msg, visible=True), |
| gr.update(value=new_code, language="python"), |
| switch_history, |
| history_to_chatbot_messages(switch_history), |
| history_to_chatbot_messages(switch_history) |
| ] |
| |
| switch_model_code_btn.click( |
| handle_switch_model_code, |
| inputs=[current_trending_model_id, code_output, history], |
| outputs=[ |
| trending_models_status, |
| code_output, |
| history, |
| history_output, |
| chat_history |
| ] |
| ) |
| |
| |
| def handle_trending_space_selection(space_id, hist, is_first): |
| """Handle when user selects a trending space""" |
| |
| if is_first: |
| return [ |
| gr.update(), |
| gr.update(), |
| gr.update(), |
| hist, |
| history_to_chatbot_messages(hist), |
| history_to_chatbot_messages(hist), |
| gr.update(), |
| False |
| ] |
| |
| if not space_id or space_id == "": |
| return [ |
| gr.update(value="Please select a space.", visible=True), |
| gr.update(), |
| gr.update(), |
| hist, |
| history_to_chatbot_messages(hist), |
| history_to_chatbot_messages(hist), |
| gr.update(visible=True), |
| False |
| ] |
| |
| |
| status, code, language, space_url = import_space_from_hf(space_id) |
| |
| |
| loaded_history = hist + [[f"Imported space: {space_id}", code]] |
| |
| |
| if language == "gradio" or language == "streamlit": |
| code_lang = "python" |
| elif language == "transformers.js": |
| code_lang = "html" |
| else: |
| code_lang = "html" |
| |
| return [ |
| gr.update(value=status, visible=True), |
| gr.update(value=code, language=code_lang), |
| gr.update(value=language), |
| loaded_history, |
| history_to_chatbot_messages(loaded_history), |
| history_to_chatbot_messages(loaded_history), |
| gr.update(value="Publish", visible=True), |
| False |
| ] |
| |
| trending_spaces_dropdown.change( |
| handle_trending_space_selection, |
| inputs=[trending_spaces_dropdown, history, spaces_first_change], |
| outputs=[ |
| trending_spaces_status, |
| code_output, |
| language_dropdown, |
| history, |
| history_output, |
| chat_history, |
| deploy_btn, |
| spaces_first_change |
| ] |
| ) |
|
|
|
|