|
|
import os |
|
|
import random |
|
|
|
|
|
import gradio as gr |
|
|
import numpy as np |
|
|
from dotenv import load_dotenv |
|
|
from gradio_client import Client |
|
|
from PIL import Image |
|
|
|
|
|
|
|
|
load_dotenv() |
|
|
|
|
|
|
|
|
HF_TOKEN = os.getenv("HF_TOKEN") |
|
|
|
|
|
|
|
|
MAX_SEED = np.iinfo(np.int32).max |
|
|
|
|
|
|
|
|
def create_prompt(params): |
|
|
""" |
|
|
Generate a prompt string for sending to the client |
|
|
|
|
|
Args: |
|
|
params (dict): Dictionary of parameters used for prompt generation |
|
|
- label (str): Comma-separated button texts, will be split into array of 4 labels |
|
|
- detail (str): Design details |
|
|
- shape (str): Button shape |
|
|
- layout (str): Layout arrangement |
|
|
- background (str): Background setting |
|
|
|
|
|
Returns: |
|
|
str: Complete prompt string for image generation |
|
|
""" |
|
|
label_str = params.get("label", "Button") |
|
|
detail = params.get("detail", "") |
|
|
shape = params.get("shape", "rounded") |
|
|
layout = params.get("layout", "horizontal_3") |
|
|
background = params.get("background", "natural") |
|
|
|
|
|
|
|
|
labels = [l.strip() for l in label_str.split(",") if l.strip()] |
|
|
if labels: |
|
|
|
|
|
while len(labels) < 4: |
|
|
labels.extend(labels) |
|
|
|
|
|
layout_config = { |
|
|
"horizontal_3": { |
|
|
"positions": ["top", "middle", "bottom"], |
|
|
"desc": "3 horizontal rows: top, middle, bottom", |
|
|
}, |
|
|
"vertical_2": { |
|
|
"positions": [ |
|
|
"left middle-valign small text", |
|
|
"right middle-valign small text", |
|
|
], |
|
|
"desc": "2 vertical tall buttons arranged side by side, vertical rectangular shape, 2 columns layout, horizontal text, small text orientation is horizontal", |
|
|
}, |
|
|
"box_2x2": { |
|
|
"positions": ["left-top", "right-top", "left-bottom", "right-bottom"], |
|
|
"desc": "2x2 grid: left-top, right-top, left-bottom, right-bottom", |
|
|
}, |
|
|
} |
|
|
shape_descriptions = { |
|
|
"box": "box shape", |
|
|
"rounded": "rounded corners", |
|
|
"oval": "oval shape", |
|
|
"free": "organic freeform shape", |
|
|
} |
|
|
|
|
|
config = layout_config.get(layout, layout_config["horizontal_3"]) |
|
|
positions = config["positions"] |
|
|
layout_desc = config["desc"] |
|
|
shape_desc = shape_descriptions.get(shape, "rounded corners") |
|
|
|
|
|
base_prompt = ( |
|
|
f"Create {len(positions)} {detail} button designs in a 1024x1024 image.\n" |
|
|
) |
|
|
base_prompt += f"Arranged in {layout_desc}.\n" |
|
|
base_prompt += f"{shape_desc.capitalize()}.\n" |
|
|
if detail: |
|
|
base_prompt += f"{detail.capitalize()} aesthetic: detailed visual elements and color palette.\n" |
|
|
|
|
|
for i, pos in enumerate(positions): |
|
|
if i < len(labels): |
|
|
base_prompt += f'{pos} button: "{labels[i]}".\n' |
|
|
else: |
|
|
base_prompt += f"{pos} button: empty text rectangle.\n" |
|
|
base_prompt += "Each button is a different design variation exploring the theme.\n" |
|
|
if background == "natural": |
|
|
base_prompt += "Natural background with subtle textures and ambient lighting.\n" |
|
|
elif background == "white": |
|
|
base_prompt += "plain white only background.\n" |
|
|
elif background == "black": |
|
|
base_prompt += "plain black only background.\n" |
|
|
else: |
|
|
base_prompt += "Clean background with proper lighting.\n" |
|
|
base_prompt += "Ultra HD, 4K, cinematic composition" |
|
|
return base_prompt |
|
|
|
|
|
|
|
|
def call_client( |
|
|
prompt, seed, randomize_seed, aspect_ratio, num_inference_steps, hf_token=None |
|
|
): |
|
|
""" |
|
|
Call the gradio client for image generation |
|
|
|
|
|
Args: |
|
|
prompt (str): Prompt text for image generation |
|
|
seed (int): Random seed value |
|
|
randomize_seed (bool): Whether to randomize the seed |
|
|
aspect_ratio (str): Image aspect ratio (e.g., "1:1", "16:9", "4:3") |
|
|
num_inference_steps (int): Number of inference steps (4-28) |
|
|
|
|
|
Returns: |
|
|
tuple: (image, seed, error) - Image object, used seed, error message |
|
|
""" |
|
|
try: |
|
|
|
|
|
resolution_map = { |
|
|
"1:1": "1024x1024 ( 1:1 )", |
|
|
"16:9": "1280x720 ( 16:9 )", |
|
|
"9:16": "720x1280 ( 9:16 )", |
|
|
} |
|
|
resolution = resolution_map.get(aspect_ratio, "1024x1024 ( 1:1 )") |
|
|
|
|
|
client = Client("Tongyi-MAI/Z-Image-Turbo", hf_token=hf_token) |
|
|
result = client.predict( |
|
|
prompt=prompt, |
|
|
resolution=resolution, |
|
|
seed=seed, |
|
|
steps=num_inference_steps, |
|
|
shift=3.0, |
|
|
random_seed=randomize_seed, |
|
|
gallery_images=[], |
|
|
api_name="/generate", |
|
|
) |
|
|
|
|
|
return result, seed, None |
|
|
except Exception as e: |
|
|
return None, seed, str(e) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def run_inference_engine( |
|
|
label, |
|
|
detail, |
|
|
shape, |
|
|
layout, |
|
|
background, |
|
|
seed, |
|
|
randomize_seed, |
|
|
aspect_ratio, |
|
|
guidance_scale, |
|
|
num_inference_steps, |
|
|
request: gr.Request, |
|
|
): |
|
|
""" |
|
|
generate UI button images |
|
|
|
|
|
Args: |
|
|
label (str): Text to display on the button,allow empty |
|
|
detail (str): Detailed design prompt |
|
|
shape (str): Button shape ("box", "rounded", "oval", "free") |
|
|
layout (str): Layout arrangement ("horizontal_3:3x1", "vertical_2:1x2", "box_2x2:2x2") |
|
|
background (str): Background setting ("natural", "white", "black") |
|
|
seed (int): Random seed value |
|
|
randomize_seed (bool): Whether to randomize the seed |
|
|
aspect_ratio (str): Image aspect ratio (use 1:1) |
|
|
guidance_scale (float): Guidance scale (use 1),no need to change |
|
|
num_inference_steps (int): Number of inference steps (use 8),no need to change |
|
|
|
|
|
Yields: |
|
|
tuple: (image, seed, status_message) - Generated image, used seed, status message |
|
|
""" |
|
|
hf_token = HF_TOKEN |
|
|
if request: |
|
|
if hasattr(request, "headers"): |
|
|
if hasattr(request.headers, "authorization"): |
|
|
hf_token = request.headers.authorization |
|
|
hf_token = hf_token.replace("Bearer", "").strip() |
|
|
|
|
|
|
|
|
yield None, seed, "Generating..." |
|
|
prompt_params = { |
|
|
"label": label, |
|
|
"detail": detail, |
|
|
"shape": shape, |
|
|
"layout": layout, |
|
|
"background": background, |
|
|
} |
|
|
prompt = create_prompt(prompt_params) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if randomize_seed: |
|
|
seed = random.randint(0, MAX_SEED) |
|
|
|
|
|
image, generated_seed, error = call_client( |
|
|
prompt, seed, randomize_seed, aspect_ratio, num_inference_steps, hf_token |
|
|
) |
|
|
|
|
|
if image is None: |
|
|
gr.Warning(f"Error: {error}") |
|
|
yield None, generated_seed, f"Error: {error}" |
|
|
else: |
|
|
|
|
|
original_image = image[0][0]["image"] |
|
|
yield (original_image, generated_seed, "") |
|
|
|
|
|
|
|
|
|
|
|
css = """ |
|
|
body { font-family: 'Helvetica Neue', Arial, sans-serif; } |
|
|
#col-container { max-width: 1200px; margin: 0 auto; padding: 20px; } |
|
|
h1 { text-align: center; font-weight: 800; color: #333; margin-bottom: 0.5em; } |
|
|
.subtitle { text-align: center; color: #666; margin-bottom: 2em; } |
|
|
|
|
|
.generate-btn { |
|
|
background: linear-gradient(90deg, #6366f1 0%, #a855f7 100%) !important; |
|
|
border: none !important; |
|
|
color: white !important; |
|
|
font-weight: bold !important; |
|
|
font-size: 1.2em !important; |
|
|
padding: 20px !important; |
|
|
border-radius: 12px !important; |
|
|
transition: all 0.3s ease; |
|
|
height: 100% !important; |
|
|
min-height: 100px; |
|
|
} |
|
|
.generate-btn:hover { |
|
|
transform: translateY(-2px); |
|
|
box-shadow: 0 5px 15px rgba(99, 102, 241, 0.4); |
|
|
} |
|
|
|
|
|
.examples-container table { |
|
|
font-size: 0.85em !important; |
|
|
margin-bottom: 0 !important; |
|
|
} |
|
|
.examples-container td { |
|
|
padding: 4px 8px !important; |
|
|
white-space: nowrap; |
|
|
overflow: hidden; |
|
|
text-overflow: ellipsis; |
|
|
max-width: 150px; |
|
|
} |
|
|
.examples-container label { |
|
|
font-weight: bold; |
|
|
color: #555; |
|
|
margin-bottom: 5px; |
|
|
display: block; |
|
|
} |
|
|
|
|
|
#result-gallery { |
|
|
border-radius: 12px; |
|
|
box-shadow: 0 10px 30px rgba(0,0,0,0.1); |
|
|
border: 1px solid #eee; |
|
|
} |
|
|
.input-group { |
|
|
background: #f9fafb; |
|
|
padding: 15px; |
|
|
border-radius: 10px; |
|
|
border: 1px solid #e5e7eb; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
""" |
|
|
|
|
|
theme = gr.themes.Soft(primary_hue="indigo", secondary_hue="slate", radius_size="md") |
|
|
|
|
|
|
|
|
example_data = [ |
|
|
[ |
|
|
"Start,Option,Exit", |
|
|
"Neon glowing cyberpunk, blue/purple gradient", |
|
|
"box", |
|
|
"horizontal_3", |
|
|
"black", |
|
|
"examples/start.webp", |
|
|
], |
|
|
[ |
|
|
"Buy", |
|
|
"Luxury gold texture, minimal elegant, serif", |
|
|
"rounded", |
|
|
"vertical_2", |
|
|
"white", |
|
|
"examples/buy.webp", |
|
|
], |
|
|
[ |
|
|
"RPG,R,P,G", |
|
|
"Wood texture, steel rim, fantasy game style", |
|
|
"free", |
|
|
"box_2x2", |
|
|
"natural", |
|
|
"examples/rpg.webp", |
|
|
], |
|
|
[ |
|
|
"Submit", |
|
|
"Modern flat design, blue gradient, clean minimal style", |
|
|
"rounded", |
|
|
"horizontal_3", |
|
|
"white", |
|
|
None, |
|
|
], |
|
|
] |
|
|
|
|
|
with gr.Blocks(css=css, theme=theme, title="UI Button Generator MCP") as demo: |
|
|
gr.Markdown("# 🎨 AI UI Button Generator") |
|
|
gr.Markdown( |
|
|
"<div class='subtitle'><p>UI button material and design concept generation tool using Z-Image-Turbo(<a href='https://huggingface.co/docs/hub/spaces-zerogpu'>Zero-GPU</a>)</p><p>Web and MCP without Header-Authorization,only few time you can try zero-gpu</p></div>" |
|
|
) |
|
|
|
|
|
with gr.Column(elem_id="col-container"): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
label = gr.Textbox( |
|
|
label="Button Text", |
|
|
placeholder="Start, OK...", |
|
|
value="Start", |
|
|
info="Text inside the button", |
|
|
render=False, |
|
|
) |
|
|
detail = gr.Textbox( |
|
|
label="Detail Prompt", |
|
|
placeholder="Design details...", |
|
|
value="Pirate theme, wood texture, gold aesthetic", |
|
|
lines=4, |
|
|
info="Design details", |
|
|
render=False, |
|
|
) |
|
|
shape = gr.Dropdown( |
|
|
label="Shape", |
|
|
choices=["box", "rounded", "oval", "free"], |
|
|
value="rounded", |
|
|
info="Shape", |
|
|
render=False, |
|
|
) |
|
|
layout = gr.Radio( |
|
|
label="Layout", |
|
|
choices=["horizontal_3", "vertical_2", "box_2x2"], |
|
|
value="horizontal_3", |
|
|
info="Layout arrangement", |
|
|
render=False, |
|
|
) |
|
|
background = gr.Dropdown( |
|
|
label="Background", |
|
|
choices=["natural", "white", "black"], |
|
|
value="natural", |
|
|
info="Background", |
|
|
render=False, |
|
|
) |
|
|
|
|
|
|
|
|
seed = gr.Slider( |
|
|
label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=42, render=False |
|
|
) |
|
|
randomize_seed = gr.Checkbox(label="Randomize seed", value=True, render=False) |
|
|
aspect_ratio = gr.Radio( |
|
|
label="Aspect Ratio", |
|
|
choices=["1:1", "16:9", "9:16"], |
|
|
value="1:1", |
|
|
render=False, |
|
|
) |
|
|
guidance_scale = gr.Slider( |
|
|
label="Guidance Scale", minimum=1.0, maximum=5.0, value=1.0, render=False |
|
|
) |
|
|
num_inference_steps = gr.Slider( |
|
|
label="Steps", minimum=4, maximum=28, value=8, render=False |
|
|
) |
|
|
|
|
|
|
|
|
result = gr.Image( |
|
|
label="Output Image", |
|
|
show_label=False, |
|
|
type="pil", |
|
|
elem_id="result-image", |
|
|
height=600, |
|
|
render=False, |
|
|
) |
|
|
status_msg = gr.Markdown(render=False) |
|
|
|
|
|
|
|
|
|
|
|
with gr.Row(equal_height=False): |
|
|
|
|
|
with gr.Column(scale=1, min_width=400): |
|
|
with gr.Group(elem_classes="input-group"): |
|
|
gr.Markdown("### 📝 Basic Settings") |
|
|
label.render() |
|
|
detail.render() |
|
|
|
|
|
with gr.Group(elem_classes="input-group"): |
|
|
gr.Markdown("### 🎨 Style & Layout") |
|
|
with gr.Row(): |
|
|
shape.render() |
|
|
background.render() |
|
|
layout.render() |
|
|
run_button = gr.Button( |
|
|
"✨ Generate\nButtons", |
|
|
variant="primary", |
|
|
elem_classes="generate-btn", |
|
|
scale=1, |
|
|
) |
|
|
|
|
|
with gr.Accordion("⚙️ Advanced Settings", open=False): |
|
|
seed.render() |
|
|
randomize_seed.render() |
|
|
aspect_ratio.render() |
|
|
with gr.Row(): |
|
|
guidance_scale.render() |
|
|
num_inference_steps.render() |
|
|
|
|
|
|
|
|
with gr.Column(scale=1): |
|
|
gr.Markdown("### 🖼️ Generated Button") |
|
|
result.render() |
|
|
status_msg.render() |
|
|
|
|
|
|
|
|
|
|
|
gr.Examples( |
|
|
examples=example_data, |
|
|
fn=run_inference_engine, |
|
|
inputs=[label, detail, shape, layout, background, result], |
|
|
outputs=[result, seed, status_msg], |
|
|
examples_per_page=3, |
|
|
run_on_click=False, |
|
|
cache_examples=False, |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
run_button.click( |
|
|
fn=run_inference_engine, |
|
|
inputs=[ |
|
|
label, |
|
|
detail, |
|
|
shape, |
|
|
layout, |
|
|
background, |
|
|
seed, |
|
|
randomize_seed, |
|
|
aspect_ratio, |
|
|
guidance_scale, |
|
|
num_inference_steps, |
|
|
], |
|
|
outputs=[result, seed, status_msg], |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch(show_error=True, mcp_server=True, share=True) |
|
|
|