akhaliq's picture
akhaliq HF Staff
add inference provider code
c2c7255
raw
history blame
105 kB
"""
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
# Monkey-patch httpx to increase timeout for OAuth
# This prevents ReadTimeout errors during HuggingFace OAuth flow
_original_client_init = httpx.AsyncClient.__init__
def _patched_client_init(self, *args, **kwargs):
# If no timeout is specified, use longer timeouts
if 'timeout' not in kwargs:
kwargs['timeout'] = httpx.Timeout(
connect=30.0, # 30 seconds for connection
read=60.0, # 60 seconds for reading response (increased from default 5s)
write=30.0, # 30 seconds for writing
pool=30.0 # 30 seconds for pool operations
)
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
)
# Main application with proper Gradio theming
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("") # Track current trending model for code switching
agent_conversation_state = gr.State({
"stage": "initial", # initial, waiting_for_answers, generating
"original_query": "",
"questions": ""
})
with gr.Sidebar() as sidebar:
login_button = gr.LoginButton()
# App link (visible on all devices)
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
)
# Unified Import section
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 HuggingFace Models section
trending_models_dropdown = gr.Dropdown(
label="🔥 Trending HuggingFace Models",
choices=[], # Will be populated on load
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 HuggingFace Spaces section
trending_spaces_dropdown = gr.Dropdown(
label="🚀 Trending HuggingFace Spaces",
choices=[], # Will be populated on load
value=None,
interactive=True,
visible=True
)
trending_spaces_status = gr.Markdown(visible=False)
# Chat history display in sidebar
chat_history = gr.Chatbot(
label="Conversation History",
type="messages",
height=300,
show_copy_button=True,
visible=True
)
# Input textbox for new messages
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 dropdown for code generation (add Streamlit and Gradio as first-class options)
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
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
)
# Removed image generation components
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 components (visible by default) ---
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")
# --- End move ---
# Removed media generation and web search UI components
# Removed media generation toggle event handlers
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")
# Removed web search availability indicator
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]
)
# --- Remove deploy/app name/sdk from bottom column ---
# (delete the gr.Column() block containing space_name_input, sdk_dropdown, deploy_btn, deploy_status)
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"
)
# Transformers.js multi-file editors (hidden by default)
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")
# Python multi-file editors (hidden by default) for Gradio/Streamlit
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")
# Static HTML multi-file editors (hidden by default). Use separate tab groups for different file counts.
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")
# React Next.js multi-file editors (hidden by default)
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")
# Removed Import Logs tab for cleaner UI
# History tab hidden per user request
# with gr.Tab("History"):
# history_output = gr.Chatbot(show_label=False, height=400, type="messages")
# Keep history_output as hidden component to maintain functionality
history_output = gr.Chatbot(show_label=False, height=400, type="messages", visible=False)
# Global generation status view (disabled placeholder)
generating_status = gr.Markdown("", visible=False)
# Unified import handler
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(), # keep import header as-is
gr.update(), # keep import button as-is
gr.update(), # language dropdown - no change
[] # chat_history
]
kind, meta = _parse_repo_or_model_url(url)
if kind == "hf_space":
status, code = load_project_from_url(url)
# Extract space info for deployment
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]]
# Determine the correct language/framework based on the imported content
code_lang = "html" # default
framework_type = "html" # for language dropdown
# Check imports to determine framework for Python code
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:
# This is a transformers.js app with the combined format
code_lang = "html" # Use html for code display
framework_type = "transformers.js" # But set dropdown to transformers.js
elif ("import " in code or "def " in code) and not ("<!DOCTYPE html>" in code or "<html" in code):
# This looks like Python code but doesn't match Streamlit/Gradio patterns
# Default to Gradio for Python spaces
code_lang = "python"
framework_type = "gradio"
# Return the updates with proper language settings
return [
gr.update(value=status, visible=True),
gr.update(value=code, language=code_lang), # Use html for transformers.js display
gr.update(value="", visible=False), # hide import textbox after submit
loaded_history,
history_to_chatbot_messages(loaded_history),
gr.update(value="Publish", visible=True),
gr.update(visible=False), # hide import header
gr.update(visible=False), # hide import button
gr.update(value=framework_type), # set language dropdown to framework type
history_to_chatbot_messages(loaded_history) # chat_history
]
else:
# GitHub or HF model → return raw snippet for LLM starting point
status, code, _ = import_repo_to_app(url)
loaded_history = [[f"Imported Repo/Model from {url}", code]]
code_lang = "python"
framework_type = "gradio" # Default to gradio for Python code
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), # hide import textbox after submit
loaded_history,
history_to_chatbot_messages(loaded_history),
gr.update(value="Publish", visible=False),
gr.update(visible=False), # hide import header
gr.update(visible=False), # hide import button
gr.update(value=framework_type), # set language dropdown to detected language
history_to_chatbot_messages(loaded_history) # chat_history
]
# Import repo/model handler
def handle_import_repo(url, framework):
status, code, preview = import_repo_to_app(url, framework)
# Heuristically set editor language based on snippet fencing or content
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}"),
]
# Event handlers
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)
# Toggle single vs multi-file editors for transformers.js and populate when switching
def toggle_editors(language, code_text):
if language == "transformers.js":
files = parse_transformers_js_output(code_text or "")
# Hide multi-file editors until all files exist; show single code until then
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), # code_output shown if editors hidden
gr.update(visible=editors_visible), # tjs_group shown only when complete
gr.update(value=files.get('index.html', '')),
gr.update(value=files.get('index.js', '')),
gr.update(value=files.get('style.css', '')),
# React group hidden
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 "")
# Show react group if we have files, else show single code editor
editors_visible = True if files else False
if editors_visible:
return [
gr.update(visible=False), # code_output hidden
gr.update(visible=False), # tjs_group hidden
gr.update(),
gr.update(),
gr.update(),
# React group shown
gr.update(visible=editors_visible), # react_group shown
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), # code_output shown
gr.update(visible=False), # tjs_group hidden
gr.update(),
gr.update(),
gr.update(),
# React group hidden
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), # code_output shown
gr.update(visible=False), # tjs_group hidden
gr.update(),
gr.update(),
gr.update(),
# React group hidden
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],
)
# Toggle Python multi-file editors for Gradio/Streamlit
def toggle_python_editors(language, code_text):
if language not in ["gradio", "streamlit"]:
return [
gr.update(visible=True), # code_output
gr.update(visible=False), # python_group_2
gr.update(visible=False), # python_group_3
gr.update(visible=False), # python_group_4
gr.update(visible=False), # python_group_5plus
# All tab and code components get empty updates
gr.update(), gr.update(), gr.update(), gr.update(), # 2-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), # 3-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), # 4-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() # 5-file group
]
files = parse_multi_file_python_output(code_text or "")
if not isinstance(files, dict) or len(files) <= 1:
# No multi-file content; keep single editor
return [
gr.update(visible=True), # code_output
gr.update(visible=False), # python_group_2
gr.update(visible=False), # python_group_3
gr.update(visible=False), # python_group_4
gr.update(visible=False), # python_group_5plus
# All tab and code components get empty updates
gr.update(), gr.update(), gr.update(), gr.update(), # 2-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), # 3-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), # 4-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() # 5-file group
]
# We have multi-file Python output: hide single editor, show appropriate group
# Order: main app first, then others sorted by name
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)
# Hide single editor, show appropriate group based on file count
updates = [gr.update(visible=False)] # code_output
if num_files == 2:
updates.extend([
gr.update(visible=True), # python_group_2
gr.update(visible=False), # python_group_3
gr.update(visible=False), # python_group_4
gr.update(visible=False), # python_group_5plus
])
# Populate 2-file group
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"),
# Empty updates for unused groups
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), # python_group_2
gr.update(visible=True), # python_group_3
gr.update(visible=False), # python_group_4
gr.update(visible=False), # python_group_5plus
])
# Populate 3-file group
path1, path2, path3 = ordered_paths[0], ordered_paths[1], ordered_paths[2]
updates.extend([
# Empty updates for 2-file group
gr.update(), gr.update(), gr.update(), gr.update(),
# Populate 3-file group
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"),
# Empty updates for unused groups
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), # python_group_2
gr.update(visible=False), # python_group_3
gr.update(visible=True), # python_group_4
gr.update(visible=False), # python_group_5plus
])
# Populate 4-file group
paths = ordered_paths[:4]
updates.extend([
# Empty updates for 2-file and 3-file groups
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(),
# Populate 4-file group
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"),
# Empty updates for 5-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
])
else: # 5+ files
updates.extend([
gr.update(visible=False), # python_group_2
gr.update(visible=False), # python_group_3
gr.update(visible=False), # python_group_4
gr.update(visible=True), # python_group_5plus
])
# Populate 5-file group (show first 5 files)
paths = ordered_paths[:5]
updates.extend([
# Empty updates for 2-file, 3-file, and 4-file groups
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(),
# Populate 5-file group
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
],
)
# Static HTML multi-file toggling and population
def toggle_static_editors(language, code_text):
# If not static HTML language, ensure single editor visible and all static groups hidden
if language != "html":
return [
gr.update(visible=True), # code_output
gr.update(visible=False), # static_group_2
gr.update(visible=False), # static_group_3
gr.update(visible=False), # static_group_4
gr.update(visible=False), # static_group_5plus
# All tab and code components get empty updates (tab, code, tab, code, ...)
gr.update(), gr.update(), gr.update(), gr.update(), # 2-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), # 3-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), # 4-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() # 5-file group
]
# Parse multi-file output first
original_files = parse_multipage_html_output(code_text or "")
# Check if we actually have multi-file content BEFORE validation
# (validate_and_autofix_files can create additional files from single-file HTML)
if not isinstance(original_files, dict) or len(original_files) <= 1:
# No genuine multi-file content; keep single editor
return [
gr.update(visible=True), # code_output
gr.update(visible=False), # static_group_2
gr.update(visible=False), # static_group_3
gr.update(visible=False), # static_group_4
gr.update(visible=False), # static_group_5plus
# All tab and code components get empty updates (tab, code, tab, code, ...)
gr.update(), gr.update(), gr.update(), gr.update(), # 2-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), # 3-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), # 4-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() # 5-file group
]
# We have genuine multi-file content - now validate and proceed with multi-file display
files = validate_and_autofix_files(original_files)
# We have multi-file static output: hide single editor, show appropriate static group
# Order: index.html first, then others sorted by path
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)
# Map extension to language
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)
# TEMPORARY FIX: For now, always keep single editor visible for HTML multi-file
# This ensures code is always visible while we debug the multi-file editors
# TODO: Remove this once multi-file editors are working properly
updates = [
gr.update(visible=True), # code_output - keep visible
gr.update(visible=False), # static_group_2 - hide multi-file editors for now
gr.update(visible=False), # static_group_3
gr.update(visible=False), # static_group_4
gr.update(visible=False), # static_group_5plus
]
# Add empty updates for all the tab and code components
updates.extend([
# All tab and code components get empty updates (tab, code, tab, code, ...)
gr.update(), gr.update(), gr.update(), gr.update(), # 2-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), # 3-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), # 4-file group
gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update() # 5-file group
])
return updates
# Respond to language change to show/hide static multi-file editors appropriately
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 "")
# Only reveal the multi-file editors when all three files are present
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(),
]
# Keep multi-file editors in sync when code_output changes and language is transformers.js
code_output.change(
sync_tjs_from_code,
inputs=[code_output, language_dropdown],
outputs=[tjs_html_code, tjs_js_code, tjs_css_code, tjs_group],
)
# Preview functions removed - replaced with deployment messaging
# The following functions are no longer used as preview has been removed:
# - preview_logic: replaced with deployment messages
# - preview_from_tjs_editors: replaced with deployment messages
# - send_to_sandbox: still used in some places but could be removed in future cleanup
# Show deployment message for transformers.js editors
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)
# Show textbox when import button is clicked
def toggle_import_textbox(url_visible):
# If textbox is already visible and has content, proceed with import
# Otherwise, just show the textbox
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, # Add chat_history to outputs
],
)
def begin_generation_ui(agent_enabled):
# In agent mode, keep sidebar open during question/task planning phase
# Only close it when actual code generation starts
if agent_enabled:
return [gr.update(), gr.update(visible=False)] # Keep sidebar as-is
else:
# Normal mode: collapse sidebar immediately
return [gr.update(open=False), gr.update(visible=False)]
def end_generation_ui():
# Open sidebar after generation; hide the status
return [gr.update(open=True), gr.update(visible=False)]
def close_sidebar_for_coding():
# Close sidebar when transitioning to actual code generation
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"""
# Check if agent mode is enabled
if agent_enabled and agent_state["stage"] == "initial":
# Agent mode - first interaction, ask questions
# Sidebar stays open during this phase
for updated_hist, chatbot_msgs in agent_generate_with_questions(
inp, sett, hist, model, lang, prov, profile, token
):
# Update agent state to track that we're waiting for answers
new_agent_state = {
"stage": "waiting_for_answers",
"original_query": inp,
"questions": updated_hist[-1][1] if updated_hist else ""
}
# Yield: code_output, history, history_output, chat_history, agent_conversation_state, sidebar
yield "", updated_hist, chatbot_msgs, chatbot_msgs, new_agent_state, gr.update()
return
elif agent_enabled and agent_state["stage"] == "waiting_for_answers":
# Agent mode - user has answered questions, now create task list and generate code
original_query = agent_state.get("original_query", "")
questions = agent_state.get("questions", "")
# Track whether we've started code generation to close sidebar
started_code_generation = False
# Process answers and generate code
for result in agent_process_answers_and_generate(
inp, original_query, questions, sett, hist, model, lang, prov,
profile, token, code_output, history_output, history
):
# Extract values from result dict
code_val = result.get(code_output, "")
hist_val = result.get(history, hist)
history_output_val = result.get(history_output, [])
# Reset agent state after generation
reset_agent_state = {
"stage": "initial",
"original_query": "",
"questions": ""
}
# Close sidebar when we start generating code (when code_output has content)
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_output, history, history_output, chat_history, agent_conversation_state, sidebar
yield code_val, hist_val, history_output_val, history_output_val, reset_agent_state, sidebar_update
return
else:
# Normal mode - direct code generation
# Sidebar was already closed by begin_generation_ui
for result in generation_code(inp, sett, hist, model, lang, prov, profile, token, code_output, history_output, history):
# generation_code yields dictionaries with component keys
# Extract the values and yield them for our outputs
code_val = result.get(code_output, "")
hist_val = result.get(history, hist)
history_output_val = result.get(history_output, [])
# Yield for: code_output, history, history_output, chat_history, agent_conversation_state, sidebar
yield code_val, hist_val, history_output_val, history_output_val, agent_state, gr.update()
# Update agent_mode_enabled state when checkbox changes
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(
# After generation, toggle editors for transformers.js and populate
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(
# After generation, toggle static multi-file editors for HTML
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(
# After generation, toggle Python multi-file editors for Gradio/Streamlit
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]
)
# Pressing Enter in the main input should trigger generation and collapse the sidebar
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(
# After generation, toggle editors for transformers.js and populate
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(
# After generation, toggle static multi-file editors for HTML
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(
# After generation, toggle Python multi-file editors for Gradio/Streamlit
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]
)
# --- Chat-based sidebar controller logic ---
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
# Show deployment message when code or language changes
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])
# Reset button text when clearing
clear_btn.click(
lambda: gr.update(value="Publish"),
outputs=[deploy_btn]
)
# Deploy to Spaces logic
def generate_random_app_name():
"""Generate a random app name that's unlikely to clash with existing apps"""
import random
import string
# Common app prefixes
prefixes = ["my", "cool", "awesome", "smart", "quick", "super", "mini", "auto", "fast", "easy"]
# Common app suffixes
suffixes = ["app", "tool", "hub", "space", "demo", "ai", "gen", "bot", "lab", "studio"]
# Random adjectives
adjectives = ["blue", "red", "green", "bright", "dark", "light", "swift", "bold", "clean", "fresh"]
# Generate different patterns
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"""
# Check if we have a previously deployed space in the history
username = profile.username if profile else None
existing_space = None
# Look for previous deployment or imported space in history
if history and username:
for user_msg, assistant_msg in history:
if assistant_msg and "✅ Deployed!" in assistant_msg:
import re
# Look for space URL pattern
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
# Look for space URL pattern
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
# Extract space name from import message
match = re.search(r'huggingface\.co/spaces/([^/\s\)]+/[^/\s\)]+)', user_msg)
if match:
imported_space = match.group(1)
# Only use imported space if user owns it (can update it)
if imported_space.startswith(f"{username}/"):
existing_space = imported_space
break
# If user doesn't own the imported space, we'll create a new one
# (existing_space remains None, triggering new deployment)
# Call the original deploy function
status = deploy_to_user_space_original(code, language, existing_space, profile, token)
# Update history if deployment was successful
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, # Pass existing space name if updating
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)
# Check if token has write permissions
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)
# Determine if this is an update or new deployment
username = profile.username
if existing_space_name and existing_space_name.startswith(f"{username}/"):
# This is an update to existing space
repo_id = existing_space_name
space_name = existing_space_name.split('/')[-1]
is_update = True
else:
# Generate a random space name for new deployment
space_name = generate_random_app_name()
repo_id = f"{username}/{space_name}"
is_update = False
# Map language to HF SDK slug
language_to_sdk_map = {
"gradio": "gradio",
"streamlit": "docker", # Use 'docker' for Streamlit Spaces
"react": "docker", # Use 'docker' for React/Next.js Spaces
"html": "static",
"transformers.js": "static", # Transformers.js uses static SDK
"comfyui": "static" # ComfyUI uses static SDK
}
sdk = language_to_sdk_map.get(language, "gradio")
# Create API client with user's token for proper authentication
api = HfApi(token=token.token)
# Only create the repo for new spaces (not updates) and non-Transformers.js, non-Streamlit SDKs
if not is_update and sdk != "docker" and language not in ["transformers.js"]:
try:
api.create_repo(
repo_id=repo_id, # e.g. username/space_name
repo_type="space",
space_sdk=sdk, # Use selected SDK
exist_ok=True # Don't error if it already exists
)
except Exception as e:
return gr.update(value=f"Error creating Space: {e}", visible=True)
# Streamlit/React/docker logic
if sdk == "docker" and language in ["streamlit", "react"]:
try:
# For new spaces, create a fresh Docker-based space
if not is_update:
# Use create_repo to create a new Docker space
from huggingface_hub import create_repo
if language == "react":
# Create a new React Docker space with docker SDK
created_repo = create_repo(
repo_id=repo_id,
repo_type="space",
space_sdk="docker",
token=token.token,
exist_ok=True
)
else:
# Create a new Streamlit Docker space
created_repo = create_repo(
repo_id=repo_id,
repo_type="space",
space_sdk="docker",
token=token.token,
exist_ok=True
)
# Handle React or Streamlit deployment
if language == "react":
# Parse React/Next.js files
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 is missing, use template
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"]
"""
# Upload React files
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:
# Determine file extension
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 and app_port to existing README
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)
# Streamlit logic - Parse multi-file structure
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)
# Verify required files exist
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 Dockerfile or requirements.txt is missing, generate them
if not has_dockerfile:
# Generate default 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:
# Generate requirements.txt from imports in the main app file
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)
# Upload Streamlit files
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:
# Determine file extension
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 and app_port to existing README
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)
# Transformers.js logic
elif language == "transformers.js":
try:
# For new spaces, duplicate the template. For updates, just verify access.
if not is_update:
# Use duplicate_space to create a transformers.js template space
from huggingface_hub import duplicate_space
# Duplicate the transformers.js template 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:
# For updates, verify we can access the existing space
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)
# Parse the code parameter which should contain the formatted transformers.js output
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)
# Upload the three files to the space (with retry logic for reliability)
import tempfile
import time
# Define files to upload
files_to_upload = [
("index.html", files['index.html']),
("index.js", files['index.js']),
("style.css", files['style.css'])
]
# Upload each file with retry logic (similar to static HTML pattern)
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:
# Permission errors won't be fixed by retrying
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: # Not the last attempt
time.sleep(2) # Wait before retrying
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 existing README (for both new and update)
add_anycoder_tag_to_readme(api, repo_id)
# For updates, trigger a space restart to ensure changes take effect
if is_update:
try:
api.restart_space(repo_id=repo_id)
except Exception as restart_error:
# Don't fail the deployment if restart fails, just log it
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:
# Handle potential RepoUrl object errors
error_msg = str(e)
if "'url'" in error_msg or "RepoUrl" in error_msg:
# For RepoUrl object issues, check if the space was actually created successfully
try:
# Check if space exists by trying to access it
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:
# Space was created successfully despite the RepoUrl error
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:
# Space was updated successfully despite the RepoUrl error
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:
# Space doesn't exist, real error
return gr.update(value=f"Error: Could not create/update space. Please try again manually at https://huggingface.co/new-space", visible=True)
except:
# Fallback to informative error with link
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)
# General error handling for both creation and updates
action_verb = "updating" if is_update else "duplicating"
return gr.update(value=f"Error {action_verb} Transformers.js space: {error_msg}", visible=True)
# Other SDKs (existing logic)
if sdk == "static":
import time
# Add anycoder tag to existing README (after repo creation)
add_anycoder_tag_to_readme(api, repo_id)
# Detect whether the HTML output is multi-file (=== filename === blocks)
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 we have multiple files (or at least a parsed index.html), upload the whole folder
if isinstance(files, dict) and len(files) > 0 and files.get('index.html'):
import tempfile
import os
try:
with tempfile.TemporaryDirectory() as tmpdir:
# Write each file preserving subdirectories if any
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)
# Upload the folder in a single commit
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 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 static app folder: {e}", visible=True)
# Fallback: single-file static HTML (upload index.html only)
file_name = "index.html"
# Special handling for ComfyUI: prettify JSON and wrap in 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 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)
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:
# Generate requirements.txt for Gradio apps and upload only if needed
import_statements = extract_import_statements(code)
requirements_content = generate_requirements_txt_with_llm(import_statements)
import tempfile
# Check if we need to upload requirements.txt
should_upload_requirements = True
if is_update:
try:
# Try to get existing requirements.txt content
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()
# Compare with new content
if existing_content == requirements_content.strip():
should_upload_requirements = False
except Exception:
# File doesn't exist or can't be accessed, so we should upload
should_upload_requirements = True
# Note: requirements.txt upload is now handled by the multi-file commit logic below
# This ensures all files are committed atomically in a single operation
# Add anycoder tag to existing README
add_anycoder_tag_to_readme(api, repo_id)
# Check if code contains multi-file format
if ('=== app.py ===' in code or '=== requirements.txt ===' in code):
# Parse multi-file format and upload each file separately
files = parse_multi_file_python_output(code)
if files:
# Ensure requirements.txt is present - auto-generate if missing
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 = []
# Create CommitOperation for each file
for filename, content in files.items():
# Clean content to ensure no stray backticks are deployed
cleaned_content = content
if filename.endswith('.txt') or filename.endswith('.py'):
# Additional safety: remove any standalone backtick lines
lines = cleaned_content.split('\n')
clean_lines = []
for line in lines:
stripped = line.strip()
# Skip lines that are just backticks
if stripped == '```' or (stripped.startswith('```') and len(stripped) <= 10):
continue
clean_lines.append(line)
cleaned_content = '\n'.join(clean_lines)
# Create temporary file
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)
# Add to operations
operations.append(CommitOperationAdd(
path_in_repo=filename,
path_or_fileobj=temp_path
))
# Commit all files at once
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"
)
# Clean up temp files
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:
# Clean up temp files on error
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:
# Fallback to single file if parsing failed
pass
# Single file upload (fallback or non-multi-file format)
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)
# Connect the deploy button to the new function
def gather_code_for_deploy(code_text, language, html_part, js_part, css_part):
# When transformers.js is selected, ensure multi-file editors are used; otherwise, return single code
if language == "transformers.js":
# Join into a combined display string for auditing; actual deploy reads editor values directly
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]
)
# Keep the old deploy method as fallback (if not logged in, user can still use the old method)
# Optionally, you can keep the old deploy_btn.click for the default method as a secondary button.
# Handle authentication state updates
# The LoginButton automatically handles OAuth flow and passes profile/token to the function
def handle_auth_update(profile: Optional[gr.OAuthProfile] = None, token: Optional[gr.OAuthToken] = None):
return update_ui_for_auth_status(profile, token)
# Update UI when login button is clicked (handles both login and logout)
login_button.click(
handle_auth_update,
inputs=[],
outputs=[input, btn],
queue=False
)
# Also update UI when the page loads in case user is already authenticated
demo.load(
handle_auth_update,
inputs=[],
outputs=[input, btn],
queue=False
)
# Load trending models when app starts
def load_trending_models():
"""Load trending models from HuggingFace Hub"""
models = get_trending_models(limit=10)
# Create choices list with display names and values as model IDs
choices = [(display, model_id) for display, model_id in models]
# Set first model as default value if available
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
)
# Load trending spaces when app starts
def load_trending_spaces():
"""Load trending spaces from HuggingFace Hub"""
spaces = get_trending_spaces(limit=10)
# Create choices list with display names and values as space IDs
choices = [(display, space_id) for display, space_id in spaces]
# Set first space as default value if available
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
)
# Handle trending model selection
def handle_trending_model_selection(model_id, hist, is_first):
"""Handle when user selects a trending model"""
# Skip import on first change (when default value is set on load)
if is_first:
return [
gr.update(), # status
gr.update(), # code_output
gr.update(), # language_dropdown
hist, # history
history_to_chatbot_messages(hist), # history_output
history_to_chatbot_messages(hist), # chat_history
False, # Set first_change to False after first trigger
gr.update(visible=False), # switch_model_code_btn
"" # current_trending_model_id
]
if not model_id or model_id == "":
return [
gr.update(value="Please select a model.", visible=True), # status
gr.update(), # code_output
gr.update(), # language_dropdown
hist, # history
history_to_chatbot_messages(hist), # history_output
history_to_chatbot_messages(hist), # chat_history
False, # Keep first_change as False
gr.update(visible=False), # switch_model_code_btn
"" # current_trending_model_id
]
# Import the model
status, code, language, model_url = import_model_from_hf(model_id)
# Add to history
loaded_history = hist + [[f"Imported model: {model_id}", code]]
# Determine code language for display
code_lang = "python"
# Check if button should be visible (both code types available)
show_switch_btn = "Found multiple code options" in status
return [
gr.update(value=status, visible=True), # status
gr.update(value=code, language=code_lang), # code_output
gr.update(value=language), # language_dropdown
loaded_history, # history
history_to_chatbot_messages(loaded_history), # history_output
history_to_chatbot_messages(loaded_history), # chat_history
False, # Keep first_change as False
gr.update(visible=show_switch_btn), # switch_model_code_btn
model_id # current_trending_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
]
)
# Handle switching between inference provider and local code
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(), # status
gr.update(), # code_output
hist, # history
history_to_chatbot_messages(hist), # history_output
history_to_chatbot_messages(hist) # chat_history
]
status_msg, new_code = switch_model_code_type(model_id, current_code)
# Update history with switch message
switch_history = hist + [[f"Switched code type for {model_id}", new_code]]
return [
gr.update(value=status_msg, visible=True), # status
gr.update(value=new_code, language="python"), # code_output
switch_history, # history
history_to_chatbot_messages(switch_history), # history_output
history_to_chatbot_messages(switch_history) # chat_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
]
)
# Handle trending space selection
def handle_trending_space_selection(space_id, hist, is_first):
"""Handle when user selects a trending space"""
# Skip import on first change (when default value is set on load)
if is_first:
return [
gr.update(), # status
gr.update(), # code_output
gr.update(), # language_dropdown
hist, # history
history_to_chatbot_messages(hist), # history_output
history_to_chatbot_messages(hist), # chat_history
gr.update(), # deploy_btn
False # Set first_change to False after first trigger
]
if not space_id or space_id == "":
return [
gr.update(value="Please select a space.", visible=True), # status
gr.update(), # code_output
gr.update(), # language_dropdown
hist, # history
history_to_chatbot_messages(hist), # history_output
history_to_chatbot_messages(hist), # chat_history
gr.update(visible=True), # deploy_btn
False # Keep first_change as False
]
# Import the space
status, code, language, space_url = import_space_from_hf(space_id)
# Add to history
loaded_history = hist + [[f"Imported space: {space_id}", code]]
# Determine code language for display based on framework
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), # status
gr.update(value=code, language=code_lang), # code_output
gr.update(value=language), # language_dropdown
loaded_history, # history
history_to_chatbot_messages(loaded_history), # history_output
history_to_chatbot_messages(loaded_history), # chat_history
gr.update(value="Publish", visible=True), # deploy_btn
False # Keep first_change as 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
]
)