faizee07 commited on
Commit
4e69b8f
Β·
verified Β·
1 Parent(s): 44d7f1a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +329 -207
app.py CHANGED
@@ -10,46 +10,62 @@ class UIDesignAgent:
10
  self.api_key = api_key
11
  self.base_url = "https://openrouter.ai/api/v1/chat/completions"
12
  self.design_system = {}
13
- self.color_palette = {}
14
- self.typography = {}
15
 
16
  def extract_design_system(self, prompt: str) -> Dict:
17
  """Extract and maintain consistent design system across pages"""
18
 
19
- system_prompt = """You are an expert UI/UX designer. Extract and define a consistent design system including:
20
- 1. Color Palette (primary, secondary, accent, neutral colors)
21
- 2. Typography (font families, sizes, weights)
22
- 3. Spacing system
23
- 4. Component styles
 
 
24
 
25
- Return as JSON format."""
26
 
27
  headers = {
28
  "Authorization": f"Bearer {self.api_key}",
29
- "Content-Type": "application/json"
 
 
30
  }
31
 
32
  data = {
33
- "model": "google/gemini-pro",
34
  "messages": [
35
  {"role": "system", "content": system_prompt},
36
  {"role": "user", "content": f"Create a design system for: {prompt}"}
37
- ]
 
 
38
  }
39
 
40
  try:
41
  response = requests.post(self.base_url, headers=headers, json=data)
42
- result = response.json()
43
- design_system_text = result['choices'][0]['message']['content']
44
-
45
- # Extract JSON from response
46
- json_match = re.search(r'\{.*\}', design_system_text, re.DOTALL)
47
- if json_match:
48
- self.design_system = json.loads(json_match.group())
49
-
 
 
 
 
 
 
 
 
 
 
 
 
50
  return self.design_system
51
  except Exception as e:
52
- print(f"Error extracting design system: {e}")
53
  return self.get_default_design_system()
54
 
55
  def get_default_design_system(self) -> Dict:
@@ -62,90 +78,110 @@ class UIDesignAgent:
62
  "background": "#ffffff",
63
  "surface": "#f8fafc",
64
  "text": "#1e293b",
65
- "textLight": "#64748b"
 
66
  },
67
  "typography": {
68
- "fontFamily": "'Inter', -apple-system, sans-serif",
69
- "h1": "48px",
70
- "h2": "36px",
71
- "h3": "24px",
72
- "body": "16px",
73
- "small": "14px"
74
  },
75
  "spacing": {
76
- "xs": "4px",
77
- "sm": "8px",
78
- "md": "16px",
79
- "lg": "24px",
80
- "xl": "32px"
 
 
 
 
 
 
 
81
  }
82
  }
83
 
84
  def generate_page_html(self, page_description: str, page_number: int) -> str:
85
  """Generate HTML for a specific page using consistent design system"""
86
 
87
- design_context = f"""
88
- Use this exact design system for consistency:
89
- Colors: {json.dumps(self.design_system.get('colors', {}))}
90
- Typography: {json.dumps(self.design_system.get('typography', {}))}
91
- Spacing: {json.dumps(self.design_system.get('spacing', {}))}
92
- """
93
 
94
- system_prompt = f"""You are an expert UI developer. Create a complete HTML page with inline CSS.
95
- {design_context}
96
 
97
- Rules:
98
- 1. Use ONLY the provided color values
99
- 2. Use ONLY the provided typography settings
100
- 3. Create responsive, modern design
101
- 4. Include all CSS inline
102
- 5. Make it interactive with hover effects
103
- 6. Ensure consistency with the design system"""
 
 
 
 
104
 
105
  headers = {
106
  "Authorization": f"Bearer {self.api_key}",
107
- "Content-Type": "application/json"
 
 
108
  }
109
 
110
  data = {
111
- "model": "google/gemini-pro",
112
  "messages": [
113
  {"role": "system", "content": system_prompt},
114
- {"role": "user", "content": f"Create page {page_number}: {page_description}. Return ONLY the HTML code."}
115
- ]
 
 
116
  }
117
 
118
  try:
119
  response = requests.post(self.base_url, headers=headers, json=data)
120
- result = response.json()
121
- html_content = result['choices'][0]['message']['content']
122
-
123
- # Extract HTML from code blocks if present
124
- html_match = re.search(r'```html\n(.*?)\n```', html_content, re.DOTALL)
125
- if html_match:
126
- html_content = html_match.group(1)
127
-
128
- # Inject design system CSS
129
- html_content = self.inject_design_system_css(html_content)
130
-
131
- return html_content
 
 
 
 
 
132
  except Exception as e:
133
- return f"<div style='color: red;'>Error generating page: {str(e)}</div>"
134
 
135
  def inject_design_system_css(self, html: str) -> str:
136
  """Inject consistent CSS variables into HTML"""
137
 
138
- css_variables = f"""
 
 
 
139
  <style>
140
  :root {{
141
- --primary: {self.design_system.get('colors', {}).get('primary', '#2563eb')};
142
- --secondary: {self.design_system.get('colors', {}).get('secondary', '#7c3aed')};
143
- --accent: {self.design_system.get('colors', {}).get('accent', '#06b6d4')};
144
- --background: {self.design_system.get('colors', {}).get('background', '#ffffff')};
145
- --surface: {self.design_system.get('colors', {}).get('surface', '#f8fafc')};
146
- --text: {self.design_system.get('colors', {}).get('text', '#1e293b')};
147
- --text-light: {self.design_system.get('colors', {}).get('textLight', '#64748b')};
148
- --font-family: {self.design_system.get('typography', {}).get('fontFamily', 'Inter, sans-serif')};
 
149
  }}
150
 
151
  * {{
@@ -161,175 +197,261 @@ class UIDesignAgent:
161
  line-height: 1.6;
162
  }}
163
 
164
- .primary-btn {{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  background-color: var(--primary);
166
  color: white;
167
- padding: 12px 24px;
168
  border: none;
169
- border-radius: 8px;
170
  cursor: pointer;
 
171
  transition: all 0.3s ease;
172
  }}
173
 
174
- .primary-btn:hover {{
175
  transform: translateY(-2px);
176
  box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
177
  }}
178
  </style>
179
  """
180
 
181
- # Insert CSS after <head> tag or at the beginning
 
 
182
  if '<head>' in html:
183
- html = html.replace('<head>', f'<head>{css_variables}')
 
 
184
  else:
185
- html = css_variables + html
186
-
 
 
 
 
 
 
 
 
 
187
  return html
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  def generate_multi_page_design(self, prompt: str) -> Tuple[List[str], Dict]:
190
  """Generate multiple pages with consistent design"""
191
 
192
- # First, extract pages from prompt
193
- pages = self.extract_pages_from_prompt(prompt)
194
-
195
- # Extract and set design system
196
  self.design_system = self.extract_design_system(prompt)
197
 
 
 
 
198
  # Generate HTML for each page
199
  generated_pages = []
200
- for i, page_desc in enumerate(pages, 1):
201
  html = self.generate_page_html(page_desc, i)
202
  generated_pages.append(html)
203
 
204
- return generated_pages, self.design_system
205
-
206
- def extract_pages_from_prompt(self, prompt: str) -> List[str]:
207
- """Extract individual page descriptions from prompt"""
208
 
209
- system_prompt = """Extract individual page descriptions from the user's request.
210
- Return as a JSON array of page descriptions."""
211
-
212
- headers = {
213
- "Authorization": f"Bearer {self.api_key}",
214
- "Content-Type": "application/json"
215
- }
216
-
217
- data = {
218
- "model": "google/gemini-pro",
219
- "messages": [
220
- {"role": "system", "content": system_prompt},
221
- {"role": "user", "content": prompt}
222
- ]
223
- }
224
-
225
- try:
226
- response = requests.post(self.base_url, headers=headers, json=data)
227
- result = response.json()
228
- pages_text = result['choices'][0]['message']['content']
229
-
230
- # Extract JSON array
231
- json_match = re.search(r'\[.*\]', pages_text, re.DOTALL)
232
- if json_match:
233
- pages = json.loads(json_match.group())
234
- return pages
235
- else:
236
- # Default to single page if extraction fails
237
- return [prompt]
238
- except Exception as e:
239
- print(f"Error extracting pages: {e}")
240
- return [prompt]
241
 
242
- # Gradio Interface
243
  def create_ui_design(prompt: str, api_key: str):
244
  """Main function to create UI designs"""
245
 
246
- if not api_key:
247
- return ["<div style='color: red;'>Please provide OpenRouter API key</div>"], "No design system"
248
-
249
- agent = UIDesignAgent(api_key)
250
- pages, design_system = agent.generate_multi_page_design(prompt)
251
-
252
- # Format design system for display
253
- design_system_display = json.dumps(design_system, indent=2)
 
254
 
255
- # Ensure we have at least 3 pages for the interface
256
- while len(pages) < 3:
257
- pages.append("<div style='padding: 20px;'>Page placeholder</div>")
 
 
 
 
 
258
 
259
- return pages[0], pages[1] if len(pages) > 1 else "", pages[2] if len(pages) > 2 else "", design_system_display
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
 
261
  # Create Gradio interface
262
- with gr.Blocks(title="AI UI Designer - Stitch Clone", theme=gr.themes.Soft()) as demo:
263
- gr.Markdown("""
264
- # 🎨 AI UI Designer (Stitch-like)
265
- ### Generate consistent multi-page website designs with AI
266
-
267
- This tool ensures consistent colors, typography, and styling across all generated pages.
268
- """)
269
-
270
- with gr.Row():
271
- with gr.Column(scale=1):
272
- api_key_input = gr.Textbox(
273
- label="OpenRouter API Key",
274
- placeholder="Enter your OpenRouter API key",
275
- type="password"
276
- )
277
-
278
- prompt_input = gr.Textbox(
279
- label="Design Prompt",
280
- placeholder="Describe your multi-page website (e.g., 'Create a 3-page website for a tech startup with: 1) Landing page with hero section, 2) About page with team section, 3) Contact page with form')",
281
- lines=5
282
- )
283
-
284
- generate_btn = gr.Button("πŸš€ Generate Design", variant="primary")
285
-
286
- design_system_output = gr.Code(
287
- label="Design System",
288
- language="json",
289
- lines=10
290
- )
291
-
292
- with gr.Column(scale=2):
293
- with gr.Tabs():
294
- with gr.TabItem("Page 1"):
295
- page1_output = gr.HTML(label="Page 1 Preview")
296
 
297
- with gr.TabItem("Page 2"):
298
- page2_output = gr.HTML(label="Page 2 Preview")
 
 
 
299
 
300
- with gr.TabItem("Page 3"):
301
- page3_output = gr.HTML(label="Page 3 Preview")
302
-
303
- gr.Markdown("""
304
- ### Features:
305
- - βœ… Consistent color scheme across all pages
306
- - βœ… Unified typography and spacing
307
- - βœ… Responsive design
308
- - βœ… Modern UI components
309
- - βœ… Design system extraction
310
-
311
- ### How to use:
312
- 1. Get your API key from [OpenRouter](https://openrouter.ai/)
313
- 2. Describe your multi-page website
314
- 3. Click Generate Design
315
- 4. View consistent designs across all pages
316
- """)
317
-
318
- generate_btn.click(
319
- fn=create_ui_design,
320
- inputs=[prompt_input, api_key_input],
321
- outputs=[page1_output, page2_output, page3_output, design_system_output]
322
- )
323
-
324
- # Example prompts
325
- gr.Examples(
326
- examples=[
327
- ["Create a 3-page SaaS website: 1) Landing page with pricing cards, 2) Features page with grid layout, 3) Contact page with form"],
328
- ["Design a portfolio website: 1) Home with hero section, 2) Projects gallery page, 3) About me page with skills"],
329
- ["Build an e-commerce site: 1) Homepage with product grid, 2) Product detail page, 3) Shopping cart page"],
330
- ],
331
- inputs=prompt_input
332
- )
333
-
334
- if __name__ == "__main__":
335
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  self.api_key = api_key
11
  self.base_url = "https://openrouter.ai/api/v1/chat/completions"
12
  self.design_system = {}
 
 
13
 
14
  def extract_design_system(self, prompt: str) -> Dict:
15
  """Extract and maintain consistent design system across pages"""
16
 
17
+ system_prompt = """You are an expert UI/UX designer. Create a consistent design system with:
18
+ - Primary color: #2563eb
19
+ - Secondary color: #7c3aed
20
+ - Accent color: #06b6d4
21
+ - Background: #ffffff
22
+ - Text: #1e293b
23
+ - Font: Inter, system-ui, sans-serif
24
 
25
+ Use these EXACT colors for ALL pages. Return the design system as JSON."""
26
 
27
  headers = {
28
  "Authorization": f"Bearer {self.api_key}",
29
+ "Content-Type": "application/json",
30
+ "HTTP-Referer": "https://huggingface.co",
31
+ "X-Title": "AI UI Designer"
32
  }
33
 
34
  data = {
35
+ "model": "openai/gpt-3.5-turbo", # Using a more reliable model
36
  "messages": [
37
  {"role": "system", "content": system_prompt},
38
  {"role": "user", "content": f"Create a design system for: {prompt}"}
39
+ ],
40
+ "temperature": 0.7,
41
+ "max_tokens": 1000
42
  }
43
 
44
  try:
45
  response = requests.post(self.base_url, headers=headers, json=data)
46
+ if response.status_code == 200:
47
+ result = response.json()
48
+ if 'choices' in result and len(result['choices']) > 0:
49
+ design_system_text = result['choices'][0]['message']['content']
50
+
51
+ # Try to extract JSON
52
+ json_match = re.search(r'\{.*\}', design_system_text, re.DOTALL)
53
+ if json_match:
54
+ try:
55
+ self.design_system = json.loads(json_match.group())
56
+ except:
57
+ self.design_system = self.get_default_design_system()
58
+ else:
59
+ self.design_system = self.get_default_design_system()
60
+ else:
61
+ self.design_system = self.get_default_design_system()
62
+ else:
63
+ print(f"API Error: {response.status_code}")
64
+ self.design_system = self.get_default_design_system()
65
+
66
  return self.design_system
67
  except Exception as e:
68
+ print(f"Error: {e}")
69
  return self.get_default_design_system()
70
 
71
  def get_default_design_system(self) -> Dict:
 
78
  "background": "#ffffff",
79
  "surface": "#f8fafc",
80
  "text": "#1e293b",
81
+ "textLight": "#64748b",
82
+ "border": "#e2e8f0"
83
  },
84
  "typography": {
85
+ "fontFamily": "'Inter', system-ui, -apple-system, sans-serif",
86
+ "h1": "2.5rem",
87
+ "h2": "2rem",
88
+ "h3": "1.5rem",
89
+ "body": "1rem",
90
+ "small": "0.875rem"
91
  },
92
  "spacing": {
93
+ "xs": "0.25rem",
94
+ "sm": "0.5rem",
95
+ "md": "1rem",
96
+ "lg": "1.5rem",
97
+ "xl": "2rem",
98
+ "xxl": "3rem"
99
+ },
100
+ "borderRadius": {
101
+ "sm": "0.25rem",
102
+ "md": "0.5rem",
103
+ "lg": "0.75rem",
104
+ "xl": "1rem"
105
  }
106
  }
107
 
108
  def generate_page_html(self, page_description: str, page_number: int) -> str:
109
  """Generate HTML for a specific page using consistent design system"""
110
 
111
+ colors = self.design_system.get('colors', self.get_default_design_system()['colors'])
112
+ typography = self.design_system.get('typography', self.get_default_design_system()['typography'])
 
 
 
 
113
 
114
+ system_prompt = f"""You are an expert UI developer. Create a complete HTML page.
 
115
 
116
+ MANDATORY DESIGN RULES:
117
+ - Primary color: {colors['primary']}
118
+ - Secondary color: {colors['secondary']}
119
+ - Background: {colors['background']}
120
+ - Text color: {colors['text']}
121
+ - Font family: {typography['fontFamily']}
122
+
123
+ Use ONLY these colors. DO NOT use any other colors.
124
+ Create a modern, responsive design with these exact colors.
125
+ Include all CSS inline in the HTML.
126
+ """
127
 
128
  headers = {
129
  "Authorization": f"Bearer {self.api_key}",
130
+ "Content-Type": "application/json",
131
+ "HTTP-Referer": "https://huggingface.co",
132
+ "X-Title": "AI UI Designer"
133
  }
134
 
135
  data = {
136
+ "model": "openai/gpt-3.5-turbo",
137
  "messages": [
138
  {"role": "system", "content": system_prompt},
139
+ {"role": "user", "content": f"Create page {page_number}: {page_description}. Use the EXACT colors provided. Return complete HTML with inline CSS."}
140
+ ],
141
+ "temperature": 0.7,
142
+ "max_tokens": 2000
143
  }
144
 
145
  try:
146
  response = requests.post(self.base_url, headers=headers, json=data)
147
+ if response.status_code == 200:
148
+ result = response.json()
149
+ if 'choices' in result and len(result['choices']) > 0:
150
+ html_content = result['choices'][0]['message']['content']
151
+
152
+ # Extract HTML from code blocks
153
+ html_match = re.search(r'```html?\n?(.*?)\n?```', html_content, re.DOTALL)
154
+ if html_match:
155
+ html_content = html_match.group(1)
156
+
157
+ # Inject consistent CSS
158
+ html_content = self.inject_design_system_css(html_content)
159
+ return html_content
160
+ else:
161
+ return self.create_error_page(f"No response for page {page_number}")
162
+ else:
163
+ return self.create_error_page(f"API Error: {response.status_code}")
164
  except Exception as e:
165
+ return self.create_error_page(f"Error: {str(e)}")
166
 
167
  def inject_design_system_css(self, html: str) -> str:
168
  """Inject consistent CSS variables into HTML"""
169
 
170
+ colors = self.design_system.get('colors', self.get_default_design_system()['colors'])
171
+ typography = self.design_system.get('typography', self.get_default_design_system()['typography'])
172
+
173
+ css_injection = f"""
174
  <style>
175
  :root {{
176
+ --primary: {colors['primary']};
177
+ --secondary: {colors['secondary']};
178
+ --accent: {colors['accent']};
179
+ --background: {colors['background']};
180
+ --surface: {colors['surface']};
181
+ --text: {colors['text']};
182
+ --text-light: {colors['textLight']};
183
+ --border: {colors['border']};
184
+ --font-family: {typography['fontFamily']};
185
  }}
186
 
187
  * {{
 
197
  line-height: 1.6;
198
  }}
199
 
200
+ h1, h2, h3, h4, h5, h6 {{
201
+ color: var(--text);
202
+ margin-bottom: 1rem;
203
+ }}
204
+
205
+ a {{
206
+ color: var(--primary);
207
+ text-decoration: none;
208
+ }}
209
+
210
+ a:hover {{
211
+ text-decoration: underline;
212
+ }}
213
+
214
+ button, .btn {{
215
  background-color: var(--primary);
216
  color: white;
217
+ padding: 0.75rem 1.5rem;
218
  border: none;
219
+ border-radius: 0.5rem;
220
  cursor: pointer;
221
+ font-family: var(--font-family);
222
  transition: all 0.3s ease;
223
  }}
224
 
225
+ button:hover, .btn:hover {{
226
  transform: translateY(-2px);
227
  box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
228
  }}
229
  </style>
230
  """
231
 
232
+ # Add viewport meta tag if not present
233
+ viewport_meta = '<meta name="viewport" content="width=device-width, initial-scale=1.0">'
234
+
235
  if '<head>' in html:
236
+ html = html.replace('<head>', f'<head>\n{viewport_meta}\n{css_injection}')
237
+ elif '<html>' in html:
238
+ html = html.replace('<html>', f'<html>\n<head>\n{viewport_meta}\n{css_injection}\n</head>')
239
  else:
240
+ html = f"""<!DOCTYPE html>
241
+ <html>
242
+ <head>
243
+ {viewport_meta}
244
+ {css_injection}
245
+ </head>
246
+ <body>
247
+ {html}
248
+ </body>
249
+ </html>"""
250
+
251
  return html
252
 
253
+ def create_error_page(self, error_message: str) -> str:
254
+ """Create an error page with consistent styling"""
255
+ return f"""
256
+ <!DOCTYPE html>
257
+ <html>
258
+ <head>
259
+ <style>
260
+ body {{
261
+ font-family: system-ui, -apple-system, sans-serif;
262
+ display: flex;
263
+ align-items: center;
264
+ justify-content: center;
265
+ height: 100vh;
266
+ margin: 0;
267
+ background: #f8fafc;
268
+ }}
269
+ .error {{
270
+ background: white;
271
+ padding: 2rem;
272
+ border-radius: 0.5rem;
273
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
274
+ max-width: 500px;
275
+ }}
276
+ h2 {{
277
+ color: #ef4444;
278
+ margin-bottom: 1rem;
279
+ }}
280
+ p {{
281
+ color: #64748b;
282
+ }}
283
+ </style>
284
+ </head>
285
+ <body>
286
+ <div class="error">
287
+ <h2>Error Generating Page</h2>
288
+ <p>{error_message}</p>
289
+ </div>
290
+ </body>
291
+ </html>
292
+ """
293
+
294
+ def extract_pages_from_prompt(self, prompt: str) -> List[str]:
295
+ """Extract individual page descriptions from prompt"""
296
+
297
+ # Simple extraction based on common patterns
298
+ pages = []
299
+
300
+ # Check for numbered pages
301
+ numbered_pattern = r'\d+\)([^0-9]+?)(?=\d+\)|$)'
302
+ matches = re.findall(numbered_pattern, prompt)
303
+
304
+ if matches:
305
+ pages = [match.strip() for match in matches]
306
+ else:
307
+ # Check for comma-separated pages
308
+ if ',' in prompt:
309
+ pages = [p.strip() for p in prompt.split(',')]
310
+ else:
311
+ # Treat as single page
312
+ pages = [prompt]
313
+
314
+ # Ensure we have at least 3 pages
315
+ while len(pages) < 3:
316
+ pages.append(f"Additional page {len(pages) + 1}")
317
+
318
+ return pages[:3] # Limit to 3 pages
319
+
320
  def generate_multi_page_design(self, prompt: str) -> Tuple[List[str], Dict]:
321
  """Generate multiple pages with consistent design"""
322
 
323
+ # Extract design system first
 
 
 
324
  self.design_system = self.extract_design_system(prompt)
325
 
326
+ # Extract pages from prompt
327
+ pages = self.extract_pages_from_prompt(prompt)
328
+
329
  # Generate HTML for each page
330
  generated_pages = []
331
+ for i, page_desc in enumerate(pages[:3], 1): # Limit to 3 pages
332
  html = self.generate_page_html(page_desc, i)
333
  generated_pages.append(html)
334
 
335
+ # Ensure we have exactly 3 pages
336
+ while len(generated_pages) < 3:
337
+ generated_pages.append(self.create_error_page("Page not specified"))
 
338
 
339
+ return generated_pages, self.design_system
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
 
 
341
  def create_ui_design(prompt: str, api_key: str):
342
  """Main function to create UI designs"""
343
 
344
+ if not api_key or api_key.strip() == "":
345
+ error_html = """
346
+ <div style='padding: 20px; color: #ef4444; background: #fee2e2; border-radius: 8px;'>
347
+ <h3>API Key Required</h3>
348
+ <p>Please provide your OpenRouter API key to generate designs.</p>
349
+ <p>Get your key from <a href='https://openrouter.ai' target='_blank'>OpenRouter.ai</a></p>
350
+ </div>
351
+ """
352
+ return error_html, error_html, error_html, "{}"
353
 
354
+ if not prompt or prompt.strip() == "":
355
+ error_html = """
356
+ <div style='padding: 20px; color: #f59e0b; background: #fef3c7; border-radius: 8px;'>
357
+ <h3>Prompt Required</h3>
358
+ <p>Please describe the website you want to create.</p>
359
+ </div>
360
+ """
361
+ return error_html, error_html, error_html, "{}"
362
 
363
+ try:
364
+ agent = UIDesignAgent(api_key)
365
+ pages, design_system = agent.generate_multi_page_design(prompt)
366
+
367
+ # Format design system for display
368
+ design_system_display = json.dumps(design_system, indent=2)
369
+
370
+ return pages[0], pages[1], pages[2], design_system_display
371
+
372
+ except Exception as e:
373
+ error_html = f"""
374
+ <div style='padding: 20px; color: #ef4444; background: #fee2e2; border-radius: 8px;'>
375
+ <h3>Generation Error</h3>
376
+ <p>{str(e)}</p>
377
+ </div>
378
+ """
379
+ return error_html, error_html, error_html, "{}"
380
 
381
  # Create Gradio interface
382
+ def create_interface():
383
+ with gr.Blocks(title="AI UI Designer - Consistent Multi-Page", theme=gr.themes.Soft()) as demo:
384
+ gr.Markdown("""
385
+ # 🎨 AI UI Designer - Consistent Multi-Page Generator
386
+ ### Generate multiple website pages with consistent design system
387
+
388
+ This tool ensures **consistent colors and styling** across all generated pages.
389
+ """)
390
+
391
+ with gr.Row():
392
+ with gr.Column(scale=1):
393
+ api_key_input = gr.Textbox(
394
+ label="OpenRouter API Key",
395
+ placeholder="sk-or-...",
396
+ type="password",
397
+ info="Get your key from openrouter.ai"
398
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
 
400
+ prompt_input = gr.Textbox(
401
+ label="Website Description",
402
+ placeholder="Example: Create a tech startup website with: 1) Landing page with hero section and features, 2) About page with team section, 3) Contact page with form",
403
+ lines=4
404
+ )
405
 
406
+ with gr.Row():
407
+ generate_btn = gr.Button("πŸš€ Generate Design", variant="primary", scale=2)
408
+ clear_btn = gr.Button("πŸ—‘οΈ Clear", scale=1)
409
+
410
+ gr.Markdown("### πŸ“‹ Design System")
411
+ design_system_output = gr.Code(
412
+ language="json",
413
+ lines=10
414
+ )
415
+
416
+ with gr.Column(scale=2):
417
+ with gr.Tabs():
418
+ with gr.TabItem("πŸ“„ Page 1"):
419
+ page1_output = gr.HTML()
420
+
421
+ with gr.TabItem("πŸ“„ Page 2"):
422
+ page2_output = gr.HTML()
423
+
424
+ with gr.TabItem("πŸ“„ Page 3"):
425
+ page3_output = gr.HTML()
426
+
427
+ gr.Markdown("""
428
+ ---
429
+ ### πŸ’‘ Tips for Best Results:
430
+ - Clearly describe each page you want
431
+ - Specify key components (hero, navbar, footer, etc.)
432
+ - Mention any specific features you need
433
+
434
+ ### 🎯 Features:
435
+ - βœ… Consistent color scheme across all pages
436
+ - βœ… Unified typography and spacing
437
+ - βœ… Modern, responsive design
438
+ - βœ… Professional UI components
439
+ """)
440
+
441
+ # Set up event handlers
442
+ generate_btn.click(
443
+ fn=create_ui_design,
444
+ inputs=[prompt_input, api_key_input],
445
+ outputs=[page1_output, page2_output, page3_output, design_system_output]
446
+ )
447
+
448
+ clear_btn.click(
449
+ fn=lambda: ("", "", "", "", "{}"),
450
+ outputs=[prompt_input, page1_output, page2_output, page3_output, design_system_output]
451
+ )
452
+
453
+ # Add examples
454
+ gr.Examples(
455
+ examples=[
456
+ "Create a SaaS website: 1) Landing page with hero and pricing cards, 2) Features page with grid layout, 3) Contact page with form",
457
+ "Design a portfolio: 1) Homepage with projects showcase, 2) About page with