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("""

LongCat-Image

""") prompt = gr.Textbox( placeholder="Describe the image you want to create...", value="Five shimmering goldfish weave through crevices between stones; four are red-and-white, while one is silver-white. By the pond's edge, a golden shaded British Shorthair cat watches them intently, counting on blind luck. Watercolor style.", lines=7, max_lines=7, label="Prompt", show_label=True, container=True, autoscroll=False, ) aspect_ratio = gr.Dropdown( choices=[ "1:1 (1024x1024)", "4:3 (1024x768)", "3:4 (768x1024)", "16:9 (1024x576)", "9:16 (576x1024)", ], value="1:1 (1024x1024)", label="Aspect Ratio", show_label=True, container=True, ) generate_btn = gr.Button( "Generate", variant="primary", size="lg", elem_classes="primary" ) # Right column - Image output with gr.Column(scale=2, elem_classes="output-section", elem_id="output-column"): output_image = gr.Image( label="Result", show_label=False, type="pil", format="png", ) # Event handlers generate_btn.click( fn=generate_image, inputs=[prompt, aspect_ratio], outputs=output_image, show_progress="full" ) prompt.submit( fn=generate_image, inputs=[prompt, aspect_ratio], outputs=output_image, show_progress="full" ) # Load event to force width with JavaScript demo.load(None, None, None, js=js_code) if __name__ == "__main__": demo.launch( mcp_server=True, share=False, show_error=True, theme=gr.themes.Soft( primary_hue=gr.themes.colors.blue, secondary_hue=gr.themes.colors.slate, neutral_hue=gr.themes.colors.gray, spacing_size=gr.themes.sizes.spacing_lg, radius_size=gr.themes.sizes.radius_lg, text_size=gr.themes.sizes.text_md, font=[gr.themes.GoogleFont("Inter"), "SF Pro Display", "-apple-system", "BlinkMacSystemFont", "system-ui", "sans-serif"], font_mono=[gr.themes.GoogleFont("JetBrains Mono"), "SF Mono", "ui-monospace", "monospace"], ).set( body_background_fill='#000000', body_background_fill_dark='#000000', button_primary_background_fill='#0071e3', button_primary_background_fill_hover='#0077ed', button_primary_text_color='#ffffff', block_background_fill='#1d1d1f', block_background_fill_dark='#1d1d1f', block_border_width='0px', block_shadow='0 2px 12px rgba(0, 0, 0, 0.4)', block_shadow_dark='0 2px 12px rgba(0, 0, 0, 0.4)', input_background_fill='#1d1d1f', input_background_fill_dark='#1d1d1f', input_border_width='1px', input_border_color='#424245', input_border_color_dark='#424245', input_shadow='none', input_shadow_focus='0 0 0 4px rgba(0, 113, 227, 0.15)', ), css=apple_css, )