import gradio as gr import numpy as np import os, random, json, spaces, torch, time import torch from transformers import AutoProcessor, AutoTokenizer from longcat_image.models import LongCatImageTransformer2DModel from longcat_image.pipelines import LongCatImagePipeline from utils.image_utils import rescale_image from utils.prompt_utils import polish_prompt # Force rebuild v20 MODEL_REPO = "meituan-longcat/LongCat-Image" MAX_SEED = np.iinfo(np.int32).max print("Loading LongCat-Image model... v12") text_processor = AutoTokenizer.from_pretrained( MODEL_REPO, subfolder = 'tokenizer' ) transformer = LongCatImageTransformer2DModel.from_pretrained( MODEL_REPO , subfolder = 'transformer', torch_dtype=torch.bfloat16, use_safetensors=True ).to("cuda") pipe = LongCatImagePipeline.from_pretrained( MODEL_REPO, transformer=transformer, text_processor=text_processor ) pipe.to("cuda", torch.bfloat16) print("Model loaded successfully!") @spaces.GPU def generate_image( prompt, aspect_ratio, progress=gr.Progress(track_tqdm=True), ): """Generate an image using LongCat-Image model.""" if not prompt.strip(): raise gr.Error("Please enter a prompt to generate an image.") # Parse aspect ratio to get dimensions aspect_ratio_map = { "1:1 (1024x1024)": (1024, 1024), "4:3 (1024x768)": (1024, 768), "3:4 (768x1024)": (768, 1024), "16:9 (1024x576)": (1024, 576), "9:16 (576x1024)": (576, 1024), } width, height = aspect_ratio_map.get(aspect_ratio, (1024, 1024)) # Generate random seed seed = random.randint(0, MAX_SEED) generator = torch.Generator().manual_seed(seed) progress(0.1, desc="Generating image...") try: image = pipe( prompt=prompt, negative_prompt="blurry ugly bad", width=width, height=height, generator=generator, guidance_scale=1.5, num_inference_steps=20, enable_prompt_rewrite=False ).images[0] progress(1.0, desc="Complete!") return image except Exception as e: raise gr.Error(f"Generation failed: {str(e)}") # Apple-style CSS for dark theme apple_css = """ /* Global Styles */ .gradio-container { max-width: 85vw !important; margin: 0 auto !important; padding: 48px 20px !important; font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', 'Roboto', sans-serif !important; } /* Disable all transitions globally to prevent layout shifts */ * { transition: none !important; animation: none !important; } /* Header */ .header-container { text-align: left; margin-bottom: 24px; } .main-title { font-size: 32px !important; font-weight: 600 !important; letter-spacing: -0.02em !important; line-height: 1.07 !important; color: #f5f5f7 !important; margin: 0 0 16px 0 !important; } /* Input Section */ .input-section { background: #1d1d1f; border-radius: 18px; padding: 32px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4); border: 1px solid #424245; } /* Textbox */ textarea { font-size: 17px !important; line-height: 1.47 !important; border-radius: 12px !important; border: 1px solid #424245 !important; padding: 12px 16px !important; background: #1d1d1f !important; color: #f5f5f7 !important; font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif !important; min-height: 200px !important; max-height: 400px !important; height: 200px !important; resize: vertical !important; overflow-y: auto !important; margin-bottom: 16px !important; } textarea:focus { border-color: #0071e3 !important; box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.15) !important; outline: none !important; } textarea::placeholder { color: #86868b !important; } /* Button */ button.primary { font-size: 17px !important; font-weight: 400 !important; padding: 12px 32px !important; border-radius: 980px !important; background: #0071e3 !important; border: none !important; color: #ffffff !important; min-height: 44px !important; letter-spacing: -0.01em !important; cursor: pointer !important; } button.primary:hover { background: #0077ed !important; } button.primary:active { opacity: 0.9 !important; } /* Output Section */ div.output-section { background: #ffffff !important; border-radius: 18px; padding: 32px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); border: none !important; overflow: hidden; display: flex; align-items: center; justify-content: center; min-height: 80vh; max-height: 90vh; will-change: auto; position: relative; } .output-section * { transform: none !important; transition: none !important; animation: none !important; } .output-section img { border-radius: 12px !important; max-width: 100% !important; max-height: 85vh !important; width: auto !important; height: auto !important; object-fit: contain !important; transform: none !important; transition: none !important; animation: none !important; backface-visibility: hidden; -webkit-backface-visibility: hidden; } /* Make progress/generation area fill more space */ .output-section > div { width: 100% !important; min-height: 75vh !important; max-height: 85vh !important; display: flex !important; align-items: center !important; justify-content: center !important; } .output-section > div > div { min-height: 75vh !important; max-height: 85vh !important; width: 100% !important; display: flex !important; align-items: center !important; justify-content: center !important; } .output-section * { max-width: 100% !important; } /* Progress */ .progress-bar { background: #0071e3 !important; border-radius: 4px !important; } /* Labels */ label { color: #f5f5f7 !important; } /* Dropdown */ .gr-dropdown { background: #1d1d1f !important; border: 1px solid #424245 !important; border-radius: 12px !important; color: #f5f5f7 !important; } select, .wrap-inner { background: #1d1d1f !important; color: #f5f5f7 !important; border-color: #424245 !important; } /* Fix column width to prevent shrinking */ .input-section { min-width: 550px !important; max-width: 550px !important; width: 550px !important; flex-shrink: 0 !important; flex-grow: 0 !important; } /* Lock the output section to fill remaining space */ .output-section { flex-grow: 1 !important; flex-shrink: 0 !important; flex-basis: auto !important; } /* Prevent Gradio columns from flexing */ .gradio-column { flex-shrink: 0 !important; } /* Stabilize row layout */ .gradio-row, div.gradio-row, .gradio-container .gradio-row, .gradio-container > .gradio-row, .gradio-container div.gradio-row { align-items: flex-start !important; flex-direction: row !important; display: flex !important; flex-wrap: nowrap !important; width: 100% !important; } /* Force columns to stay inline */ .gradio-row > .gradio-column, .gradio-row > div { display: inline-flex !important; vertical-align: top !important; } /* First column - input section */ .gradio-row > .gradio-column:first-child, .gradio-row > div:first-child { width: 550px !important; min-width: 550px !important; max-width: 550px !important; flex: 0 0 550px !important; } /* Second column - output section */ .gradio-row > .gradio-column:last-child, .gradio-row > div:last-child { flex: 1 1 auto !important; min-width: 0 !important; } /* Hide progress indicator in input section */ .input-section .progress-container, .input-section [class*="progress-bar"], .input-section [class*="progress-text"], .input-section [class*="progress-level"], .input-section .progress, .input-section .eta-bar { display: none !important; visibility: hidden !important; height: 0 !important; overflow: hidden !important; } /* Override responsive behavior */ @media (max-width: 2000px) { .gradio-row, div.gradio-row, .gradio-container .gradio-row, .gradio-container > .gradio-row { flex-direction: row !important; flex-wrap: nowrap !important; display: flex !important; } .gradio-row > .gradio-column, .gradio-row > div { display: inline-flex !important; } .gradio-row > .gradio-column:first-child, .gradio-row > div:first-child { width: 550px !important; min-width: 550px !important; max-width: 550px !important; flex: 0 0 550px !important; } .gradio-row > .gradio-column:last-child, .gradio-row > div:last-child { flex: 1 1 auto !important; min-width: 0 !important; } } /* Responsive text sizing only */ @media (max-width: 734px) { .main-title { font-size: 28px !important; } .gradio-container { padding: 32px 16px !important; } .input-section, .output-section { padding: 24px !important; } /* FORCE horizontal layout even on mobile */ .gradio-row, div.gradio-row { flex-direction: row !important; flex-wrap: nowrap !important; } } /* Hide Gradio footer */ footer { display: none !important; } .footer { display: none !important; } """ # JavaScript to force horizontal layout js_code = """ function() { function forceHorizontalLayout() { // Set container width const container = document.querySelector('.gradio-container'); if (container) { container.style.maxWidth = '85vw'; container.style.width = '85vw'; } // Target the main row specifically const mainRow = document.getElementById('main-row'); if (mainRow) { mainRow.style.flexDirection = 'row'; mainRow.style.flexWrap = 'nowrap'; mainRow.style.display = 'flex'; mainRow.style.width = '100%'; } // Force ALL rows to stay horizontal const rows = document.querySelectorAll('.gradio-row'); rows.forEach(row => { row.style.flexDirection = 'row'; row.style.flexWrap = 'nowrap'; row.style.display = 'flex'; }); // Target specific columns const inputCol = document.getElementById('input-column'); if (inputCol) { inputCol.style.width = '550px'; inputCol.style.minWidth = '550px'; inputCol.style.maxWidth = '550px'; inputCol.style.flex = '0 0 550px'; inputCol.style.display = 'inline-flex'; inputCol.style.flexDirection = 'column'; } const outputCol = document.getElementById('output-column'); if (outputCol) { outputCol.style.flex = '1 1 auto'; outputCol.style.minWidth = '0'; outputCol.style.display = 'inline-flex'; outputCol.style.flexDirection = 'column'; } // Fallback: force all column children of rows const columns = document.querySelectorAll('.gradio-row > .gradio-column, .gradio-row > div'); columns.forEach((col, index) => { if (index === 0) { col.style.width = '550px'; col.style.minWidth = '550px'; col.style.maxWidth = '550px'; col.style.flex = '0 0 550px'; } else if (index === 1) { col.style.flex = '1 1 auto'; col.style.minWidth = '0'; } col.style.display = 'inline-flex'; }); } // Run immediately forceHorizontalLayout(); // Run again after delays to override Gradio's dynamic changes setTimeout(forceHorizontalLayout, 100); setTimeout(forceHorizontalLayout, 500); setTimeout(forceHorizontalLayout, 1000); setTimeout(forceHorizontalLayout, 2000); // Set up mutation observer to reapply on DOM changes const observer = new MutationObserver(forceHorizontalLayout); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); } """ # Create the interface with gr.Blocks( title="LongCat-Image", fill_height=False, ) as demo: # Two-column layout with gr.Row(equal_height=False, variant="panel", elem_id="main-row"): # Left column - Input controls (fixed width) with gr.Column(scale=0, min_width=550, elem_classes="input-section", elem_id="input-column"): # Title above prompt box gr.HTML("""