Spaces:
Build error
Build error
File size: 10,948 Bytes
c7bb72d afdcc12 aabfb43 afdcc12 aabfb43 afdcc12 aabfb43 afdcc12 aabfb43 afdcc12 aabfb43 afdcc12 aabfb43 afdcc12 aabfb43 afdcc12 aabfb43 afdcc12 aabfb43 afdcc12 aabfb43 afdcc12 aabfb43 ae01dc3 aabfb43 afdcc12 aabfb43 afdcc12 aabfb43 afdcc12 aabfb43 afdcc12 ae01dc3 afdcc12 ae01dc3 afdcc12 ae01dc3 afdcc12 ae01dc3 afdcc12 ae01dc3 aabfb43 4e69b8f aabfb43 4e69b8f ae01dc3 aabfb43 ae01dc3 afdcc12 4e69b8f ae01dc3 afdcc12 d5172be ae01dc3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
import gradio as gr
import json
import re
from typing import Dict, List, Tuple
import os
# Important: Make sure you have python-dotenv installed (`pip install python-dotenv`)
# and a .env file if you run locally. On HF Spaces, use secrets.
from dotenv import load_dotenv
# Try to import CrewAI libraries. If they fail, it means they are not installed.
try:
from crewai import Agent, Task, Crew, Process
from langchain_openai import ChatOpenAI
except ImportError:
raise ImportError(
"CrewAI or LangChain not installed. Please ensure your requirements.txt is correct and dependencies are installed."
)
load_dotenv()
# --- Configuration ---
# You can change the model here. Claude 3 Haiku is fast, cheap, and very capable.
DEFAULT_MODEL = "minimax/minimax-m2:free"
# --- Agent Definitions ---
# 1. Color Specialist Agent
color_specialist = Agent(
role='Expert Color Palette Designer',
goal="""Select a visually appealing and contextually appropriate color palette
based on the user's website description. The palette must include primary,
secondary, background, surface, and text colors.""",
backstory="""You are a world-renowned graphic designer with a deep understanding of
color theory and its psychological impact. You create palettes that are not
only beautiful but also functional and accessible.""",
verbose=True,
allow_delegation=False
)
# 2. Typography Specialist Agent
typography_specialist = Agent(
role='Master Typographer for Digital Interfaces',
goal="""Define a complete and harmonious typography system, including a web-safe
font family and a responsive size scale (h1, h2, body, small).""",
backstory="""With decades of experience, you excel at choosing fonts that are readable,
stylish, and appropriate for a brand's voice. You create clear typographic
hierarchies that work perfectly on the web.""",
verbose=True,
allow_delegation=False
)
# 3. Design System Architect Agent (The "Stitcher")
design_system_architect = Agent(
role='Senior UI Design System Architect',
goal="""Consolidate the color and typography schemes into a single, comprehensive,
and developer-friendly JSON design system. Also add standard 'spacing' and
'borderRadius' systems.""",
backstory="""You are a meticulous senior UI designer who creates robust and scalable
design systems. Your work ensures absolute consistency and serves as the single
source of truth for the entire development team.""",
verbose=True,
allow_delegation=False
)
# 4. Web UI Developer Agent
web_developer = Agent(
role='Expert Frontend Developer specializing in Web UI',
goal="""Generate a complete, single HTML file for a given page description,
strictly adhering to the provided design system. The code must be clean,
responsive, and modern.""",
backstory="""You are a pixel-perfect frontend developer who translates design systems
into clean, responsive, and maintainable HTML and CSS. You never deviate from
the design specifications.""",
verbose=True,
allow_delegation=False
)
# --- Task Definitions ---
def create_design_tasks(prompt):
task_colors = Task(
description=f"""Analyze the user's prompt: '{prompt}'.
Generate a JSON object for the color palette.
The JSON must contain keys: 'primary', 'secondary', 'background', 'surface', 'text'.""",
expected_output="A single, valid JSON object containing the color palette.",
agent=color_specialist
)
task_typography = Task(
description=f"""Analyze the user's prompt: '{prompt}'.
Generate a JSON object for the typography system.
It must include 'fontFamily', and sizes for 'h1', 'h2', 'body', 'small'.""",
expected_output="A single, valid JSON object for the typography system.",
agent=typography_specialist
)
task_architect = Task(
description="""Take the color palette and typography system from the specialists.
Combine them into a single, final JSON object. Also, add standard 'spacing'
(sm, md, lg) and 'borderRadius' (md, lg) systems.
Your final output must be ONLY this complete JSON object and nothing else.""",
expected_output="A single, valid JSON object representing the complete design system.",
agent=design_system_architect,
context=[task_colors, task_typography]
)
return task_colors, task_typography, task_architect
def create_developer_task(design_system_json, page_description):
return Task(
description=f"""Using the final design system provided below, create a complete HTML file
for the following page: '{page_description}'. All CSS must be in a single `<style>` tag
in the `<head>`.
FINAL DESIGN SYSTEM:
{design_system_json}""",
expected_output="The complete, raw HTML code for the webpage, starting with `<!DOCTYPE html>` and nothing else.",
agent=web_developer
)
# --- Main Gradio Function ---
def create_ui_design(prompt: str, api_key: str, progress=gr.Progress(track_tqdm=True)):
if not api_key:
error_msg = "API Key is missing. Please enter your OpenRouter API key."
return (error_msg,) * 5
os.environ["OPENROUTER_API_KEY"] = api_key
# Configure the LLM to use OpenRouter
openrouter_llm = ChatOpenAI(
model=DEFAULT_MODEL,
api_key=api_key,
base_url="https://openrouter.ai/api/v1"
)
try:
progress(0.1, desc="Briefing the design team...")
design_tasks = create_design_tasks(prompt)
design_crew = Crew(
agents=[color_specialist, typography_specialist, design_system_architect],
tasks=list(design_tasks),
process=Process.sequential,
verbose=2,
llm=openrouter_llm
)
progress(0.3, desc="Architecting the Design System...")
design_system_json_string = design_crew.kickoff(inputs={'prompt': prompt})
design_system = json.loads(design_system_json_string)
progress(0.6, desc="Briefing the Web Developer...")
page_descriptions = re.findall(r'\d+\)([^0-9]+?)(?=\d+\)|$)', prompt) or [prompt]
while len(page_descriptions) < 3:
page_descriptions.append(f"Page {len(page_descriptions)+1} placeholder")
generated_pages = []
developer_crew = Crew(agents=[web_developer], tasks=[], process=Process.sequential, llm=openrouter_llm)
for i, page_desc in enumerate(page_descriptions[:3]):
progress(0.6 + (i * 0.1), desc=f"Developer is building Page {i+1}...")
dev_task = create_developer_task(design_system_json_string, page_desc)
developer_crew.tasks = [dev_task]
html_output = developer_crew.kickoff()
generated_pages.append(html_output)
progress(0.9, desc="Creating Visualizer...")
visualizer_html = visualize_design_system(design_system)
progress(1.0, desc="Done!")
return generated_pages[0], generated_pages[1], generated_pages[2], design_system_json_string, visualizer_html
except Exception as e:
error_html = f"An error occurred with the CrewAI agents: {str(e)}. Check the logs for details."
print(error_html)
return (error_html,) * 5
# --- Visualizer Function ---
def visualize_design_system(design_system: Dict) -> str:
if not design_system or not isinstance(design_system, dict): return "<div class='error-box'>No valid design system to display.</div>"
colors = design_system.get('colors', {})
typography = design_system.get('typography', {})
html = """<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Design System</title><style>body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background-color:#f9fafb;color:#374151;padding:1.5rem;}.section{margin-bottom:2rem;}h2{font-size:1.5rem;font-weight:600;border-bottom:1px solid #e5e7eb;padding-bottom:.5rem;margin-bottom:1rem;}.swatch-container{display:flex;flex-wrap:wrap;gap:1rem;}.swatch{width:100px;height:100px;border-radius:.5rem;display:flex;flex-direction:column;justify-content:flex-end;padding:.5rem;font-size:.8rem;box-shadow:0 2px 4px rgba(0,0,0,0.1);}.swatch .name{font-weight:600;}.type-container .sample{padding:1rem;background-color:#fff;border:1px solid #e5e7eb;border-radius:.5rem;margin-bottom:1rem;}</style></head><body>"""
html += "<div class='section'><h2>Color Palette</h2><div class='swatch-container'>"
for name, hex_val in colors.items():
try: r,g,b=int(hex_val[1:3],16),int(hex_val[3:5],16),int(hex_val[5:7],16); text_color='#fff' if (0.299*r+0.587*g+0.114*b)/255<0.5 else '#000'
except: text_color='#000'
html+=f'<div class="swatch" style="background-color:{hex_val};color:{text_color};"><span class="name">{name.capitalize()}</span><span>{hex_val}</span></div>'
html += "</div></div>"
font_family = typography.get('fontFamily', 'sans-serif')
html += f"<div class='section'><h2>Typography</h2><p>Font: <code>{font_family}</code></p>"
for name, sample_text in {'h1':'Heading 1','h2':'Heading 2','body':'This is a paragraph of body text.'}.items():
html+=f'<div class="sample"><div style="font-family:{font_family};font-size:{typography.get(name,"1rem")};">{sample_text}</div></div>'
html += "</div></body></html>"
return html
# --- Gradio Interface ---
def create_interface():
with gr.Blocks(title="CrewAI UI Designer", theme=gr.themes.Soft()) as demo:
gr.Markdown("# π€ CrewAI Powered UI Designer")
with gr.Row():
with gr.Column(scale=1):
api_key_input = gr.Textbox(label="OpenRouter API Key", placeholder="sk-or-...", type="password")
prompt_input = gr.Textbox(label="Website Description", placeholder="e.g., A dark-mode, futuristic site for a synthwave musician: 1) landing page, 2) music, 3) tour dates", lines=4)
generate_btn = gr.Button("π Assemble the Crew!", variant="primary")
with gr.Tabs():
with gr.TabItem("Visualizer"): design_system_visualizer = gr.HTML()
with gr.TabItem("JSON"): design_system_output = gr.Code(language="json", lines=15)
with gr.Column(scale=2):
with gr.Tabs():
with gr.TabItem("π Page 1"): page1_output = gr.HTML()
with gr.TabItem("π Page 2"): page2_output = gr.HTML()
with gr.TabItem("π Page 3"): page3_output = gr.HTML()
generate_btn.click(fn=create_ui_design, inputs=[prompt_input, api_key_input], outputs=[page1_output, page2_output, page3_output, design_system_output, design_system_visualizer])
return demo
if __name__ == "__main__":
demo = create_interface()
demo.launch() |