Spaces:
Running
Running
fixes
Browse files
app.py
CHANGED
@@ -69,6 +69,40 @@ Always respond with code that can be executed or rendered directly.
|
|
69 |
|
70 |
Always output only the HTML code inside a ```html ... ``` code block, and do not include any explanations or extra text. Do NOT add the language name at the top of the code output."""
|
71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
# Stricter prompt for GLM-4.5V to ensure a complete, runnable HTML document with no escaped characters
|
73 |
GLM45V_HTML_SYSTEM_PROMPT = """You are an expert front-end developer.
|
74 |
|
@@ -794,7 +828,6 @@ for _m in AVAILABLE_MODELS:
|
|
794 |
break
|
795 |
if DEFAULT_MODEL is None and AVAILABLE_MODELS:
|
796 |
DEFAULT_MODEL = AVAILABLE_MODELS[0]
|
797 |
-
|
798 |
DEMO_LIST = [
|
799 |
{
|
800 |
"title": "Todo App",
|
@@ -1335,6 +1368,27 @@ def parse_multipage_html_output(text: str) -> Dict[str, str]:
|
|
1335 |
files[name] = content
|
1336 |
return files
|
1337 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1338 |
def validate_and_autofix_files(files: Dict[str, str]) -> Dict[str, str]:
|
1339 |
"""Ensure minimal contract for multi-file sites; auto-fix missing pieces.
|
1340 |
|
@@ -1486,6 +1540,19 @@ def inline_multipage_into_single_preview(files: Dict[str, str]) -> str:
|
|
1486 |
|
1487 |
return doc
|
1488 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1489 |
def parse_svelte_output(text):
|
1490 |
"""Parse Svelte output to extract individual files"""
|
1491 |
files = {
|
@@ -1560,9 +1627,8 @@ def process_image_for_model(image):
|
|
1560 |
|
1561 |
buffer = io.BytesIO()
|
1562 |
image.save(buffer, format='PNG')
|
1563 |
-
img_str = base64.b64encode(buffer.getvalue()).decode()
|
1564 |
return f"data:image/png;base64,{img_str}"
|
1565 |
-
|
1566 |
def generate_image_with_qwen(prompt: str, image_index: int = 0) -> str:
|
1567 |
"""Generate image using Qwen image model via Hugging Face InferenceClient with optimized data URL"""
|
1568 |
try:
|
@@ -2307,7 +2373,6 @@ def create_music_replacement_blocks_text_to_music(html_content: str, prompt: str
|
|
2307 |
|
2308 |
# If no <body>, just append
|
2309 |
return f"{SEARCH_START}\n\n{DIVIDER}\n{audio_html}\n{REPLACE_END}"
|
2310 |
-
|
2311 |
def create_image_replacement_blocks_from_input_image(html_content: str, user_prompt: str, input_image_data, max_images: int = 1) -> str:
|
2312 |
"""Create search/replace blocks using image-to-image generation with a provided input image.
|
2313 |
|
@@ -2480,12 +2545,33 @@ def create_video_replacement_blocks_from_input_image(html_content: str, user_pro
|
|
2480 |
print("[Image2Video] No <body> tag; appending video via replacement block")
|
2481 |
return f"{SEARCH_START}\n\n{DIVIDER}\n{video_html}\n{REPLACE_END}"
|
2482 |
|
2483 |
-
def apply_generated_media_to_html(html_content: str, user_prompt: str, enable_text_to_image: bool, enable_image_to_image: bool, input_image_data, image_to_image_prompt: str | None = None, text_to_image_prompt: str | None = None, enable_image_to_video: bool = False, image_to_video_prompt: str | None = None, session_id: Optional[str] = None, enable_text_to_video: bool = False, text_to_video_prompt: str
|
2484 |
-
"""Apply text
|
2485 |
|
2486 |
-
|
|
|
|
|
|
|
2487 |
"""
|
2488 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2489 |
try:
|
2490 |
print(
|
2491 |
f"[MediaApply] enable_i2v={enable_image_to_video}, enable_i2i={enable_image_to_image}, "
|
@@ -2495,7 +2581,16 @@ def apply_generated_media_to_html(html_content: str, user_prompt: str, enable_te
|
|
2495 |
if enable_image_to_video and input_image_data is not None and (result.strip().startswith('<!DOCTYPE html>') or result.strip().startswith('<html')):
|
2496 |
i2v_prompt = (image_to_video_prompt or user_prompt or "").strip()
|
2497 |
print(f"[MediaApply] Running image-to-video with prompt len={len(i2v_prompt)}")
|
2498 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2499 |
if blocks_v:
|
2500 |
print("[MediaApply] Applying image-to-video replacement blocks")
|
2501 |
before_len = len(result)
|
@@ -2513,46 +2608,94 @@ def apply_generated_media_to_html(html_content: str, user_prompt: str, enable_te
|
|
2513 |
result = result_after
|
2514 |
else:
|
2515 |
print("[MediaApply] No i2v replacement blocks generated")
|
|
|
|
|
|
|
2516 |
return result
|
2517 |
|
2518 |
# If text-to-video is enabled, insert a generated video (no input image required) and return.
|
2519 |
if enable_text_to_video and (result.strip().startswith('<!DOCTYPE html>') or result.strip().startswith('<html')):
|
2520 |
t2v_prompt = (text_to_video_prompt or user_prompt or "").strip()
|
2521 |
print(f"[MediaApply] Running text-to-video with prompt len={len(t2v_prompt)}")
|
2522 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2523 |
if blocks_tv:
|
2524 |
print("[MediaApply] Applying text-to-video replacement blocks")
|
2525 |
result = apply_search_replace_changes(result, blocks_tv)
|
2526 |
else:
|
2527 |
print("[MediaApply] No t2v replacement blocks generated")
|
|
|
|
|
|
|
2528 |
return result
|
2529 |
|
2530 |
# If text-to-music is enabled, insert a generated audio player near the top of body and return.
|
2531 |
if enable_text_to_music and (result.strip().startswith('<!DOCTYPE html>') or result.strip().startswith('<html')):
|
2532 |
t2m_prompt = (text_to_music_prompt or user_prompt or "").strip()
|
2533 |
print(f"[MediaApply] Running text-to-music with prompt len={len(t2m_prompt)}")
|
2534 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2535 |
if blocks_tm:
|
2536 |
print("[MediaApply] Applying text-to-music replacement blocks")
|
2537 |
result = apply_search_replace_changes(result, blocks_tm)
|
2538 |
else:
|
2539 |
print("[MediaApply] No t2m replacement blocks generated")
|
|
|
|
|
|
|
2540 |
return result
|
2541 |
|
2542 |
# If an input image is provided and image-to-image is enabled, we only replace one image
|
2543 |
# and skip text-to-image to satisfy the requirement to replace exactly the number of uploaded images.
|
2544 |
if enable_image_to_image and input_image_data is not None and (result.strip().startswith('<!DOCTYPE html>') or result.strip().startswith('<html')):
|
2545 |
i2i_prompt = (image_to_image_prompt or user_prompt or "").strip()
|
2546 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2547 |
if blocks2:
|
2548 |
result = apply_search_replace_changes(result, blocks2)
|
|
|
|
|
|
|
2549 |
return result
|
2550 |
|
2551 |
if enable_text_to_image and (result.strip().startswith('<!DOCTYPE html>') or result.strip().startswith('<html')):
|
2552 |
t2i_prompt = (text_to_image_prompt or user_prompt or "").strip()
|
2553 |
print(f"[MediaApply] Running text-to-image with prompt len={len(t2i_prompt)}")
|
2554 |
-
# Single-image flow for text-to-image
|
2555 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2556 |
if blocks:
|
2557 |
print("[MediaApply] Applying text-to-image replacement blocks")
|
2558 |
result = apply_search_replace_changes(result, blocks)
|
@@ -2561,6 +2704,9 @@ def apply_generated_media_to_html(html_content: str, user_prompt: str, enable_te
|
|
2561 |
print("[MediaApply] Exception during media application:")
|
2562 |
traceback.print_exc()
|
2563 |
return html_content
|
|
|
|
|
|
|
2564 |
return result
|
2565 |
|
2566 |
def create_multimodal_message(text, image=None):
|
@@ -2990,7 +3136,6 @@ def demo_card_click(e: gr.EventData):
|
|
2990 |
except (KeyError, IndexError, AttributeError) as e:
|
2991 |
# Return the first demo description as fallback
|
2992 |
return DEMO_LIST[0]['description']
|
2993 |
-
|
2994 |
def extract_text_from_image(image_path):
|
2995 |
"""Extract text from image using OCR"""
|
2996 |
try:
|
@@ -3535,7 +3680,7 @@ This will help me create a better design for you."""
|
|
3535 |
# Apply media generation (images/video/music)
|
3536 |
print("[Generate] Applying post-generation media to GLM-4.5 HTML output")
|
3537 |
final_content = apply_generated_media_to_html(
|
3538 |
-
|
3539 |
query,
|
3540 |
enable_text_to_image=enable_image_generation,
|
3541 |
enable_image_to_image=enable_image_to_image,
|
@@ -3747,9 +3892,10 @@ This will help me create a better design for you."""
|
|
3747 |
|
3748 |
preview_val = None
|
3749 |
if language == "html":
|
3750 |
-
|
|
|
3751 |
_mpf2 = validate_and_autofix_files(_mpf2)
|
3752 |
-
preview_val = send_to_sandbox(inline_multipage_into_single_preview(_mpf2)) if _mpf2.get('index.html') else send_to_sandbox(
|
3753 |
elif language == "python" and is_streamlit_code(final_content):
|
3754 |
preview_val = send_streamlit_to_stlite(final_content)
|
3755 |
yield {
|
@@ -4200,9 +4346,10 @@ This will help me create a better design for you."""
|
|
4200 |
_history.append([query, final_content])
|
4201 |
preview_val = None
|
4202 |
if language == "html":
|
4203 |
-
|
|
|
4204 |
_mpf = validate_and_autofix_files(_mpf)
|
4205 |
-
preview_val = send_to_sandbox(inline_multipage_into_single_preview(_mpf)) if _mpf.get('index.html') else send_to_sandbox(
|
4206 |
elif language == "python" and is_streamlit_code(final_content):
|
4207 |
preview_val = send_streamlit_to_stlite(final_content)
|
4208 |
elif language == "gradio" or (language == "python" and is_gradio_code(final_content)):
|
@@ -4457,7 +4604,6 @@ def wrap_html_in_gradio_app(html_code):
|
|
4457 |
'if __name__ == "__main__":\n'
|
4458 |
' demo.launch()\n'
|
4459 |
)
|
4460 |
-
|
4461 |
def deploy_to_spaces(code):
|
4462 |
if not code or not code.strip():
|
4463 |
return # Do nothing if code is empty
|
@@ -5406,6 +5552,8 @@ with gr.Blocks(
|
|
5406 |
visible=False
|
5407 |
)
|
5408 |
|
|
|
|
|
5409 |
def on_image_to_image_toggle(toggled, beta_enabled):
|
5410 |
# Only show in classic mode (beta disabled)
|
5411 |
vis = bool(toggled) and not bool(beta_enabled)
|
@@ -5827,7 +5975,6 @@ with gr.Blocks(
|
|
5827 |
import re
|
5828 |
match = re.search(r"https?://[^\s]+", text or "")
|
5829 |
return match.group(0) if match else None
|
5830 |
-
|
5831 |
def apply_chat_command(message, chat_messages):
|
5832 |
# Support plain text or dict from MultimodalTextbox
|
5833 |
text = message if isinstance(message, str) else (message.get("text", "") if isinstance(message, dict) else "")
|
@@ -6148,7 +6295,6 @@ with gr.Blocks(
|
|
6148 |
|
6149 |
restart_message = f"""
|
6150 |
🎨 **Theme saved:** {theme_name}
|
6151 |
-
|
6152 |
⚠️ **Restart required** to fully apply the new theme.
|
6153 |
|
6154 |
**Why restart is needed:** Gradio themes are set during application startup and cannot be changed dynamically at runtime. This ensures all components are properly styled with consistent theming.
|
|
|
69 |
|
70 |
Always output only the HTML code inside a ```html ... ``` code block, and do not include any explanations or extra text. Do NOT add the language name at the top of the code output."""
|
71 |
|
72 |
+
def llm_place_media(html_content: str, media_html_tag: str, media_kind: str = "image") -> str:
|
73 |
+
"""Ask a lightweight model to produce search/replace blocks that insert media_html_tag in the best spot.
|
74 |
+
|
75 |
+
The model must return ONLY our block format using SEARCH_START/DIVIDER/REPLACE_END.
|
76 |
+
"""
|
77 |
+
try:
|
78 |
+
client = get_inference_client("Qwen/Qwen3-Coder-480B-A35B-Instruct", "auto")
|
79 |
+
system_prompt = (
|
80 |
+
"You are a code editor. Insert the provided media tag into the given HTML in the most semantically appropriate place.\n"
|
81 |
+
"Prefer replacing a placeholder <img> or a hero area; otherwise insert inside <body> near primary content.\n"
|
82 |
+
"Return ONLY search/replace blocks using the exact markers: <<<<<<< SEARCH, =======, >>>>>>> REPLACE.\n"
|
83 |
+
"Do NOT include any commentary. Ensure the SEARCH block matches exact lines from the input.\n"
|
84 |
+
)
|
85 |
+
user_payload = (
|
86 |
+
"HTML Document:\n" + html_content + "\n\n" +
|
87 |
+
f"Media ({media_kind}):\n" + media_html_tag + "\n\n" +
|
88 |
+
"Produce search/replace blocks now."
|
89 |
+
)
|
90 |
+
messages = [
|
91 |
+
{"role": "system", "content": system_prompt},
|
92 |
+
{"role": "user", "content": user_payload},
|
93 |
+
]
|
94 |
+
completion = client.chat.completions.create(
|
95 |
+
model="Qwen/Qwen3-Coder-480B-A35B-Instruct",
|
96 |
+
messages=messages,
|
97 |
+
max_tokens=2000,
|
98 |
+
temperature=0.2,
|
99 |
+
)
|
100 |
+
text = (completion.choices[0].message.content or "") if completion and completion.choices else ""
|
101 |
+
return text.strip()
|
102 |
+
except Exception as e:
|
103 |
+
print(f"[LLMPlaceMedia] Fallback due to error: {e}")
|
104 |
+
return ""
|
105 |
+
|
106 |
# Stricter prompt for GLM-4.5V to ensure a complete, runnable HTML document with no escaped characters
|
107 |
GLM45V_HTML_SYSTEM_PROMPT = """You are an expert front-end developer.
|
108 |
|
|
|
828 |
break
|
829 |
if DEFAULT_MODEL is None and AVAILABLE_MODELS:
|
830 |
DEFAULT_MODEL = AVAILABLE_MODELS[0]
|
|
|
831 |
DEMO_LIST = [
|
832 |
{
|
833 |
"title": "Todo App",
|
|
|
1368 |
files[name] = content
|
1369 |
return files
|
1370 |
|
1371 |
+
def format_multipage_output(files: Dict[str, str]) -> str:
|
1372 |
+
"""Format a dict of files back into === filename === sections.
|
1373 |
+
|
1374 |
+
Ensures `index.html` appears first if present; others follow sorted by path.
|
1375 |
+
"""
|
1376 |
+
if not isinstance(files, dict) or not files:
|
1377 |
+
return ""
|
1378 |
+
ordered_paths = []
|
1379 |
+
if 'index.html' in files:
|
1380 |
+
ordered_paths.append('index.html')
|
1381 |
+
for path in sorted(files.keys()):
|
1382 |
+
if path == 'index.html':
|
1383 |
+
continue
|
1384 |
+
ordered_paths.append(path)
|
1385 |
+
parts: list[str] = []
|
1386 |
+
for path in ordered_paths:
|
1387 |
+
parts.append(f"=== {path} ===")
|
1388 |
+
# Avoid trailing extra newlines to keep blocks compact
|
1389 |
+
parts.append((files.get(path) or '').rstrip())
|
1390 |
+
return "\n".join(parts)
|
1391 |
+
|
1392 |
def validate_and_autofix_files(files: Dict[str, str]) -> Dict[str, str]:
|
1393 |
"""Ensure minimal contract for multi-file sites; auto-fix missing pieces.
|
1394 |
|
|
|
1540 |
|
1541 |
return doc
|
1542 |
|
1543 |
+
def extract_html_document(text: str) -> str:
|
1544 |
+
"""Return substring starting from the first <!DOCTYPE html> or <html> if present, else original text.
|
1545 |
+
|
1546 |
+
This ignores prose or planning notes before the actual HTML so previews don't break.
|
1547 |
+
"""
|
1548 |
+
if not text:
|
1549 |
+
return text
|
1550 |
+
lower = text.lower()
|
1551 |
+
idx = lower.find("<!doctype html")
|
1552 |
+
if idx == -1:
|
1553 |
+
idx = lower.find("<html")
|
1554 |
+
return text[idx:] if idx != -1 else text
|
1555 |
+
|
1556 |
def parse_svelte_output(text):
|
1557 |
"""Parse Svelte output to extract individual files"""
|
1558 |
files = {
|
|
|
1627 |
|
1628 |
buffer = io.BytesIO()
|
1629 |
image.save(buffer, format='PNG')
|
1630 |
+
img_str = base64.b64encode(buffer.getvalue()).decode('utf-8')
|
1631 |
return f"data:image/png;base64,{img_str}"
|
|
|
1632 |
def generate_image_with_qwen(prompt: str, image_index: int = 0) -> str:
|
1633 |
"""Generate image using Qwen image model via Hugging Face InferenceClient with optimized data URL"""
|
1634 |
try:
|
|
|
2373 |
|
2374 |
# If no <body>, just append
|
2375 |
return f"{SEARCH_START}\n\n{DIVIDER}\n{audio_html}\n{REPLACE_END}"
|
|
|
2376 |
def create_image_replacement_blocks_from_input_image(html_content: str, user_prompt: str, input_image_data, max_images: int = 1) -> str:
|
2377 |
"""Create search/replace blocks using image-to-image generation with a provided input image.
|
2378 |
|
|
|
2545 |
print("[Image2Video] No <body> tag; appending video via replacement block")
|
2546 |
return f"{SEARCH_START}\n\n{DIVIDER}\n{video_html}\n{REPLACE_END}"
|
2547 |
|
2548 |
+
def apply_generated_media_to_html(html_content: str, user_prompt: str, enable_text_to_image: bool, enable_image_to_image: bool, input_image_data, image_to_image_prompt: str | None = None, text_to_image_prompt: str | None = None, enable_image_to_video: bool = False, image_to_video_prompt: str | None = None, session_id: Optional[str] = None, enable_text_to_video: bool = False, text_to_video_prompt: Optional[str] = None, enable_text_to_music: bool = False, text_to_music_prompt: Optional[str] = None) -> str:
|
2549 |
+
"""Apply text/image/video/music replacements to HTML content.
|
2550 |
|
2551 |
+
- Works with single-document HTML strings
|
2552 |
+
- Also supports multi-page outputs formatted as === filename === sections by
|
2553 |
+
applying changes to the HTML entrypoint (index.html if present) and
|
2554 |
+
returning the updated multi-page text.
|
2555 |
"""
|
2556 |
+
# Detect multi-page sections and choose an entry HTML to modify
|
2557 |
+
is_multipage = False
|
2558 |
+
multipage_files: Dict[str, str] = {}
|
2559 |
+
entry_html_path: Optional[str] = None
|
2560 |
+
try:
|
2561 |
+
multipage_files = parse_multipage_html_output(html_content) or {}
|
2562 |
+
if multipage_files:
|
2563 |
+
is_multipage = True
|
2564 |
+
if 'index.html' in multipage_files:
|
2565 |
+
entry_html_path = 'index.html'
|
2566 |
+
else:
|
2567 |
+
html_paths = [p for p in multipage_files.keys() if p.lower().endswith('.html')]
|
2568 |
+
entry_html_path = html_paths[0] if html_paths else None
|
2569 |
+
except Exception:
|
2570 |
+
is_multipage = False
|
2571 |
+
multipage_files = {}
|
2572 |
+
entry_html_path = None
|
2573 |
+
|
2574 |
+
result = multipage_files.get(entry_html_path, html_content) if is_multipage and entry_html_path else html_content
|
2575 |
try:
|
2576 |
print(
|
2577 |
f"[MediaApply] enable_i2v={enable_image_to_video}, enable_i2i={enable_image_to_image}, "
|
|
|
2581 |
if enable_image_to_video and input_image_data is not None and (result.strip().startswith('<!DOCTYPE html>') or result.strip().startswith('<html')):
|
2582 |
i2v_prompt = (image_to_video_prompt or user_prompt or "").strip()
|
2583 |
print(f"[MediaApply] Running image-to-video with prompt len={len(i2v_prompt)}")
|
2584 |
+
try:
|
2585 |
+
video_html_tag = generate_video_from_image(input_image_data, i2v_prompt, session_id=session_id)
|
2586 |
+
if not (video_html_tag or "").startswith("Error"):
|
2587 |
+
blocks_v = llm_place_media(result, video_html_tag, media_kind="video")
|
2588 |
+
else:
|
2589 |
+
blocks_v = ""
|
2590 |
+
except Exception:
|
2591 |
+
blocks_v = ""
|
2592 |
+
if not blocks_v:
|
2593 |
+
blocks_v = create_video_replacement_blocks_from_input_image(result, i2v_prompt, input_image_data, session_id=session_id)
|
2594 |
if blocks_v:
|
2595 |
print("[MediaApply] Applying image-to-video replacement blocks")
|
2596 |
before_len = len(result)
|
|
|
2608 |
result = result_after
|
2609 |
else:
|
2610 |
print("[MediaApply] No i2v replacement blocks generated")
|
2611 |
+
if is_multipage and entry_html_path:
|
2612 |
+
multipage_files[entry_html_path] = result
|
2613 |
+
return format_multipage_output(multipage_files)
|
2614 |
return result
|
2615 |
|
2616 |
# If text-to-video is enabled, insert a generated video (no input image required) and return.
|
2617 |
if enable_text_to_video and (result.strip().startswith('<!DOCTYPE html>') or result.strip().startswith('<html')):
|
2618 |
t2v_prompt = (text_to_video_prompt or user_prompt or "").strip()
|
2619 |
print(f"[MediaApply] Running text-to-video with prompt len={len(t2v_prompt)}")
|
2620 |
+
try:
|
2621 |
+
video_html_tag = generate_video_from_text(t2v_prompt, session_id=session_id)
|
2622 |
+
if not (video_html_tag or "").startswith("Error"):
|
2623 |
+
blocks_tv = llm_place_media(result, video_html_tag, media_kind="video")
|
2624 |
+
else:
|
2625 |
+
blocks_tv = ""
|
2626 |
+
except Exception:
|
2627 |
+
blocks_tv = ""
|
2628 |
+
if not blocks_tv:
|
2629 |
+
blocks_tv = create_video_replacement_blocks_text_to_video(result, t2v_prompt, session_id=session_id)
|
2630 |
if blocks_tv:
|
2631 |
print("[MediaApply] Applying text-to-video replacement blocks")
|
2632 |
result = apply_search_replace_changes(result, blocks_tv)
|
2633 |
else:
|
2634 |
print("[MediaApply] No t2v replacement blocks generated")
|
2635 |
+
if is_multipage and entry_html_path:
|
2636 |
+
multipage_files[entry_html_path] = result
|
2637 |
+
return format_multipage_output(multipage_files)
|
2638 |
return result
|
2639 |
|
2640 |
# If text-to-music is enabled, insert a generated audio player near the top of body and return.
|
2641 |
if enable_text_to_music and (result.strip().startswith('<!DOCTYPE html>') or result.strip().startswith('<html')):
|
2642 |
t2m_prompt = (text_to_music_prompt or user_prompt or "").strip()
|
2643 |
print(f"[MediaApply] Running text-to-music with prompt len={len(t2m_prompt)}")
|
2644 |
+
try:
|
2645 |
+
audio_html_tag = generate_music_from_text(t2m_prompt, session_id=session_id)
|
2646 |
+
if not (audio_html_tag or "").startswith("Error"):
|
2647 |
+
blocks_tm = llm_place_media(result, audio_html_tag, media_kind="audio")
|
2648 |
+
else:
|
2649 |
+
blocks_tm = ""
|
2650 |
+
except Exception:
|
2651 |
+
blocks_tm = ""
|
2652 |
+
if not blocks_tm:
|
2653 |
+
blocks_tm = create_music_replacement_blocks_text_to_music(result, t2m_prompt, session_id=session_id)
|
2654 |
if blocks_tm:
|
2655 |
print("[MediaApply] Applying text-to-music replacement blocks")
|
2656 |
result = apply_search_replace_changes(result, blocks_tm)
|
2657 |
else:
|
2658 |
print("[MediaApply] No t2m replacement blocks generated")
|
2659 |
+
if is_multipage and entry_html_path:
|
2660 |
+
multipage_files[entry_html_path] = result
|
2661 |
+
return format_multipage_output(multipage_files)
|
2662 |
return result
|
2663 |
|
2664 |
# If an input image is provided and image-to-image is enabled, we only replace one image
|
2665 |
# and skip text-to-image to satisfy the requirement to replace exactly the number of uploaded images.
|
2666 |
if enable_image_to_image and input_image_data is not None and (result.strip().startswith('<!DOCTYPE html>') or result.strip().startswith('<html')):
|
2667 |
i2i_prompt = (image_to_image_prompt or user_prompt or "").strip()
|
2668 |
+
try:
|
2669 |
+
image_html_tag = generate_image_to_image(input_image_data, i2i_prompt)
|
2670 |
+
if not (image_html_tag or "").startswith("Error"):
|
2671 |
+
blocks2 = llm_place_media(result, image_html_tag, media_kind="image")
|
2672 |
+
else:
|
2673 |
+
blocks2 = ""
|
2674 |
+
except Exception:
|
2675 |
+
blocks2 = ""
|
2676 |
+
if not blocks2:
|
2677 |
+
blocks2 = create_image_replacement_blocks_from_input_image(result, i2i_prompt, input_image_data, max_images=1)
|
2678 |
if blocks2:
|
2679 |
result = apply_search_replace_changes(result, blocks2)
|
2680 |
+
if is_multipage and entry_html_path:
|
2681 |
+
multipage_files[entry_html_path] = result
|
2682 |
+
return format_multipage_output(multipage_files)
|
2683 |
return result
|
2684 |
|
2685 |
if enable_text_to_image and (result.strip().startswith('<!DOCTYPE html>') or result.strip().startswith('<html')):
|
2686 |
t2i_prompt = (text_to_image_prompt or user_prompt or "").strip()
|
2687 |
print(f"[MediaApply] Running text-to-image with prompt len={len(t2i_prompt)}")
|
2688 |
+
# Single-image flow for text-to-image (LLM placement first, fallback deterministic)
|
2689 |
+
try:
|
2690 |
+
image_html_tag = generate_image_with_qwen(t2i_prompt, 0)
|
2691 |
+
if not (image_html_tag or "").startswith("Error"):
|
2692 |
+
blocks = llm_place_media(result, image_html_tag, media_kind="image")
|
2693 |
+
else:
|
2694 |
+
blocks = ""
|
2695 |
+
except Exception:
|
2696 |
+
blocks = ""
|
2697 |
+
if not blocks:
|
2698 |
+
blocks = create_image_replacement_blocks_text_to_image_single(result, t2i_prompt)
|
2699 |
if blocks:
|
2700 |
print("[MediaApply] Applying text-to-image replacement blocks")
|
2701 |
result = apply_search_replace_changes(result, blocks)
|
|
|
2704 |
print("[MediaApply] Exception during media application:")
|
2705 |
traceback.print_exc()
|
2706 |
return html_content
|
2707 |
+
if is_multipage and entry_html_path:
|
2708 |
+
multipage_files[entry_html_path] = result
|
2709 |
+
return format_multipage_output(multipage_files)
|
2710 |
return result
|
2711 |
|
2712 |
def create_multimodal_message(text, image=None):
|
|
|
3136 |
except (KeyError, IndexError, AttributeError) as e:
|
3137 |
# Return the first demo description as fallback
|
3138 |
return DEMO_LIST[0]['description']
|
|
|
3139 |
def extract_text_from_image(image_path):
|
3140 |
"""Extract text from image using OCR"""
|
3141 |
try:
|
|
|
3680 |
# Apply media generation (images/video/music)
|
3681 |
print("[Generate] Applying post-generation media to GLM-4.5 HTML output")
|
3682 |
final_content = apply_generated_media_to_html(
|
3683 |
+
clean_code,
|
3684 |
query,
|
3685 |
enable_text_to_image=enable_image_generation,
|
3686 |
enable_image_to_image=enable_image_to_image,
|
|
|
3892 |
|
3893 |
preview_val = None
|
3894 |
if language == "html":
|
3895 |
+
safe_preview = extract_html_document(final_content)
|
3896 |
+
_mpf2 = parse_multipage_html_output(safe_preview)
|
3897 |
_mpf2 = validate_and_autofix_files(_mpf2)
|
3898 |
+
preview_val = send_to_sandbox(inline_multipage_into_single_preview(_mpf2)) if _mpf2.get('index.html') else send_to_sandbox(safe_preview)
|
3899 |
elif language == "python" and is_streamlit_code(final_content):
|
3900 |
preview_val = send_streamlit_to_stlite(final_content)
|
3901 |
yield {
|
|
|
4346 |
_history.append([query, final_content])
|
4347 |
preview_val = None
|
4348 |
if language == "html":
|
4349 |
+
safe_preview = extract_html_document(final_content)
|
4350 |
+
_mpf = parse_multipage_html_output(safe_preview)
|
4351 |
_mpf = validate_and_autofix_files(_mpf)
|
4352 |
+
preview_val = send_to_sandbox(inline_multipage_into_single_preview(_mpf)) if _mpf.get('index.html') else send_to_sandbox(safe_preview)
|
4353 |
elif language == "python" and is_streamlit_code(final_content):
|
4354 |
preview_val = send_streamlit_to_stlite(final_content)
|
4355 |
elif language == "gradio" or (language == "python" and is_gradio_code(final_content)):
|
|
|
4604 |
'if __name__ == "__main__":\n'
|
4605 |
' demo.launch()\n'
|
4606 |
)
|
|
|
4607 |
def deploy_to_spaces(code):
|
4608 |
if not code or not code.strip():
|
4609 |
return # Do nothing if code is empty
|
|
|
5552 |
visible=False
|
5553 |
)
|
5554 |
|
5555 |
+
# LLM-guided media placement is now always on (no toggle in UI)
|
5556 |
+
|
5557 |
def on_image_to_image_toggle(toggled, beta_enabled):
|
5558 |
# Only show in classic mode (beta disabled)
|
5559 |
vis = bool(toggled) and not bool(beta_enabled)
|
|
|
5975 |
import re
|
5976 |
match = re.search(r"https?://[^\s]+", text or "")
|
5977 |
return match.group(0) if match else None
|
|
|
5978 |
def apply_chat_command(message, chat_messages):
|
5979 |
# Support plain text or dict from MultimodalTextbox
|
5980 |
text = message if isinstance(message, str) else (message.get("text", "") if isinstance(message, dict) else "")
|
|
|
6295 |
|
6296 |
restart_message = f"""
|
6297 |
🎨 **Theme saved:** {theme_name}
|
|
|
6298 |
⚠️ **Restart required** to fully apply the new theme.
|
6299 |
|
6300 |
**Why restart is needed:** Gradio themes are set during application startup and cannot be changed dynamically at runtime. This ensures all components are properly styled with consistent theming.
|