dina1 commited on
Commit
eaebbd9
·
verified ·
1 Parent(s): 231b76c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +99 -107
app.py CHANGED
@@ -3,6 +3,7 @@ import gradio as gr
3
  from html2image import Html2Image
4
  import uuid
5
  import os
 
6
 
7
  # -----------------------
8
  # Configure Gemini API
@@ -16,23 +17,13 @@ model = genai.GenerativeModel("gemini-2.0-flash")
16
  hti = Html2Image(browser_executable="/usr/bin/chromium")
17
 
18
  # -----------------------
19
- # Fluent-style SVG Icons
20
  # -----------------------
21
- SVG_FLUENT = {
22
- "hamburger": """<svg viewBox="0 0 24 24" fill="#ccc"><rect y="4" width="24" height="2"/><rect y="11" width="24" height="2"/><rect y="18" width="24" height="2"/></svg>""",
23
- "home": """<svg viewBox="0 0 24 24" fill="currentColor"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>""",
24
- "recent": """<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 8v5l3.5 3.5.8-.8L13 12.7V8z"/><path d="M12 2a10 10 0 1010 10A10 10 0 0012 2zm0 18a8 8 0 118-8 8 8 0 01-8 8z"/></svg>""",
25
- "pinned": """<svg viewBox="0 0 24 24" fill="currentColor"><path d="M16 4l-1 5h-6l-1-5H6l1 6-2 2v2h14v-2l-2-2 1-6zM11 22h2v-4h-2z"/></svg>"""
26
- }
27
-
28
- # -----------------------
29
- # Default Chart Snippets
30
- # -----------------------
31
- DEFAULT_BAR_CHART = """<div class='chart-container'><div class='chart-title'>Sample Bar Chart</div></div>"""
32
- DEFAULT_LINE_CHART = """<div class='chart-container'><div class='chart-title'>Sample Line Chart</div></div>"""
33
- DEFAULT_PIE_CHART = """<div class='chart-container'><div class='chart-title'>Sample Pie Chart</div></div>"""
34
- DEFAULT_STACKED_BAR_CHART = """<div class='chart-container'><div class='chart-title'>Sample Stacked Bar Chart</div></div>"""
35
- DEFAULT_DONUT_CHART = """<div class='chart-container'><div class='chart-title'>Sample Donut Chart</div></div>"""
36
 
37
  # -----------------------
38
  # Base PowerApps Layout
@@ -46,11 +37,11 @@ body {
46
  font-family: 'Segoe UI', sans-serif;
47
  background: #f5f5f5;
48
  }
49
-
50
- /* ---------- shared ---------- */
51
  .sidebar {
52
  width: 240px;
53
  height: 100vh;
 
 
54
  float: left;
55
  display: flex;
56
  flex-direction: column;
@@ -61,11 +52,13 @@ body {
61
  padding: 12px 16px;
62
  font-size: 16px;
63
  font-weight: bold;
 
64
  }
65
  .sidebar-header svg {
66
  width: 20px;
67
  height: 20px;
68
  margin-right: 8px;
 
69
  }
70
  .sidebar-item {
71
  padding: 10px 18px;
@@ -78,6 +71,22 @@ body {
78
  width: 18px;
79
  height: 18px;
80
  margin-right: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  }
82
  .sidebar-section {
83
  margin-top: 14px;
@@ -85,35 +94,8 @@ body {
85
  font-size: 12px;
86
  font-weight: bold;
87
  text-transform: uppercase;
 
88
  }
89
-
90
- /* ---------- dark theme ---------- */
91
- .dark-theme {
92
- background: #2d2d2d;
93
- color: #fff;
94
- }
95
- .dark-theme .sidebar-header { background: #1f1f1f; }
96
- .dark-theme .sidebar-item:hover { background: #3a3a3a; }
97
- .dark-theme .sidebar-section { color: #aaa; }
98
- .dark-theme .sidebar-item.active { background: #0078d4; }
99
- .dark-theme .sidebar-item.active svg,
100
- .dark-theme .sidebar-item.active span { color: #fff; fill: #fff; }
101
-
102
- /* ---------- light theme ---------- */
103
- .light-theme {
104
- background: #f2f2f2;
105
- color: #222;
106
- }
107
- .light-theme .sidebar-header { background: #e0e0e0; color: #222; }
108
- .light-theme .sidebar-item:hover { background: #ddd; }
109
- .light-theme .sidebar-section { color: #666; }
110
- .light-theme .sidebar-item.active {
111
- background: #0078d4;
112
- }
113
- .light-theme .sidebar-item.active span,
114
- .light-theme .sidebar-item.active svg { color: #fff; fill: #fff; }
115
-
116
- /* ---------- topbar / content ---------- */
117
  .topbar {
118
  height: 50px;
119
  background: #0078d4;
@@ -123,7 +105,10 @@ body {
123
  padding: 0 20px;
124
  font-weight: bold;
125
  }
126
- .main { margin-left: 240px; padding: 20px; }
 
 
 
127
  .card-grid {
128
  display: grid;
129
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
@@ -143,55 +128,79 @@ body {
143
  <body>
144
  {user_sidebar}
145
  <div class="topbar">PowerApps Mockup</div>
146
- <div class="main">
147
- {user_content}
148
- </div>
149
  </body>
150
  </html>
151
  """
152
 
153
  # -----------------------
154
- # Function: Generate Sidebar
155
  # -----------------------
156
- def generate_sidebar(user_prompt):
157
- theme_class = "light-theme" if "light" in user_prompt.lower() else "dark-theme"
 
 
 
 
158
 
159
- sidebar_html = f"<div class='sidebar {theme_class}'>"
 
 
 
 
160
  sidebar_html += f"<div class='sidebar-header'>{SVG_FLUENT['hamburger']} App Title</div>"
161
 
162
- # Default fixed items (SVG icons)
163
- sidebar_html += f"<div class='sidebar-item active'>{SVG_FLUENT['home']} <span>Home</span></div>"
164
- sidebar_html += f"<div class='sidebar-item'>{SVG_FLUENT['recent']} <span>Recent</span></div>"
165
- sidebar_html += f"<div class='sidebar-item'>{SVG_FLUENT['pinned']} <span>Pinned</span></div>"
 
166
 
167
  sidebar_html += "<div class='sidebar-section'>My Work</div>"
168
 
169
- # Ask Gemini to generate both labels and SVGs for My Work
170
- system_prompt = (
171
- "You are a PowerApps UI design assistant. "
172
- "Based on the user's requirements, create 3–6 sidebar items for the 'My Work' section. "
173
- "Each item must include a descriptive label and its Fluent-style SVG icon. "
174
- "Output strictly in the format: Label | <svg ...></svg> , Label | <svg ...></svg>"
 
 
 
 
 
 
175
  )
176
 
177
  try:
178
- response = model.generate_content(system_prompt + "\n" + user_prompt)
179
- generated_text = response.text.strip()
180
-
181
- items = []
182
- for pair in generated_text.split(","):
183
- parts = pair.strip().split("|")
184
- if len(parts) == 2:
185
- label = parts[0].strip()
186
- svg = parts[1].strip()
187
- items.append((label, svg))
188
-
189
- for label, svg in items:
190
- sidebar_html += f"<div class='sidebar-item'>{svg}<span>{label}</span></div>"
191
-
192
- except Exception as e:
193
- # fallback
194
- sidebar_html += "<div class='sidebar-item'><svg viewBox='0 0 24 24'><circle cx='12' cy='12' r='10' fill='#0078d4'/></svg><span>Dashboard</span></div>"
 
 
 
 
 
 
 
 
 
 
 
195
 
196
  sidebar_html += "</div>"
197
  return sidebar_html
@@ -203,41 +212,24 @@ def generate_mockup(user_prompt):
203
  system_prompt = (
204
  "You are a Power Apps mockup generator. "
205
  "Generate ONLY the inner HTML for the main screen content. "
206
- "Do NOT include <html>, <head>, or <body>. "
207
  )
208
-
209
  try:
210
  response = model.generate_content(system_prompt + "\n" + user_prompt)
211
  user_html = response.text.strip()
212
-
213
- # Clean Gemini formatting
214
  if user_html.startswith("```"):
215
- user_html = user_html.split("```")[1]
216
- user_html = user_html.replace("html", "").strip()
217
-
218
- # Fallback chart injection
219
- if "bar chart" in user_prompt.lower() and "<div class=\"bar-chart\"" not in user_html:
220
- user_html += DEFAULT_BAR_CHART
221
- if "line chart" in user_prompt.lower() and "<div class=\"line-chart\"" not in user_html:
222
- user_html += DEFAULT_LINE_CHART
223
- if "pie chart" in user_prompt.lower() and "<div class=\"pie-chart\"" not in user_html:
224
- user_html += DEFAULT_PIE_CHART
225
- if "stacked bar" in user_prompt.lower() and "<div class=\"stacked-bar-chart\"" not in user_html:
226
- user_html += DEFAULT_STACKED_BAR_CHART
227
- if "donut" in user_prompt.lower() and "<div class=\"donut-chart\"" not in user_html:
228
- user_html += DEFAULT_DONUT_CHART
229
 
230
  sidebar_html = generate_sidebar(user_prompt)
231
-
232
  full_html = BASE_TEMPLATE.replace("{user_sidebar}", sidebar_html).replace("{user_content}", user_html)
233
 
234
- unique_id = str(uuid.uuid4())
235
- html_file, img_file = f"mockup_{unique_id}.html", f"mockup_{unique_id}.png"
236
- with open(html_file, "w", encoding="utf-8") as f:
237
  f.write(full_html)
238
 
239
- hti.screenshot(html_file=html_file, save_as=img_file)
240
- return img_file
241
 
242
  except Exception as e:
243
  return f"Error: {e}"
@@ -246,11 +238,11 @@ def generate_mockup(user_prompt):
246
  # Gradio UI
247
  # -----------------------
248
  with gr.Blocks() as demo:
249
- gr.Markdown("## ⚡ Power Apps Mockup Generator (Gemini + Gradio)")
250
  user_input = gr.Textbox(
251
  label="Describe your Power Apps mockup screen",
252
  lines=4,
253
- placeholder="Example: A dark dashboard with reports and approvals section"
254
  )
255
  generate_btn = gr.Button("Generate Mockup")
256
  image_output = gr.Image(label="Mockup Preview")
 
3
  from html2image import Html2Image
4
  import uuid
5
  import os
6
+ import re
7
 
8
  # -----------------------
9
  # Configure Gemini API
 
17
  hti = Html2Image(browser_executable="/usr/bin/chromium")
18
 
19
  # -----------------------
20
+ # Default Chart Snippets (Fallbacks)
21
  # -----------------------
22
+ DEFAULT_BAR_CHART = """ ... same as before ... """
23
+ DEFAULT_LINE_CHART = """ ... same as before ... """
24
+ DEFAULT_PIE_CHART = """ ... same as before ... """
25
+ DEFAULT_STACKED_BAR_CHART = """ ... same as before ... """
26
+ DEFAULT_DONUT_CHART = """ ... same as before ... """
 
 
 
 
 
 
 
 
 
 
27
 
28
  # -----------------------
29
  # Base PowerApps Layout
 
37
  font-family: 'Segoe UI', sans-serif;
38
  background: #f5f5f5;
39
  }
 
 
40
  .sidebar {
41
  width: 240px;
42
  height: 100vh;
43
+ background: #2d2d2d;
44
+ color: #fff;
45
  float: left;
46
  display: flex;
47
  flex-direction: column;
 
52
  padding: 12px 16px;
53
  font-size: 16px;
54
  font-weight: bold;
55
+ background: #1f1f1f;
56
  }
57
  .sidebar-header svg {
58
  width: 20px;
59
  height: 20px;
60
  margin-right: 8px;
61
+ fill: #ccc;
62
  }
63
  .sidebar-item {
64
  padding: 10px 18px;
 
71
  width: 18px;
72
  height: 18px;
73
  margin-right: 10px;
74
+ fill: #c5c5c5;
75
+ }
76
+ .sidebar-item span {
77
+ color: #ccc;
78
+ }
79
+ .sidebar-item:hover {
80
+ background: #3a3a3a;
81
+ }
82
+ .sidebar-item.active {
83
+ background: #0078d4;
84
+ }
85
+ .sidebar-item.active span {
86
+ color: #fff;
87
+ }
88
+ .sidebar-item.active svg {
89
+ fill: #fff;
90
  }
91
  .sidebar-section {
92
  margin-top: 14px;
 
94
  font-size: 12px;
95
  font-weight: bold;
96
  text-transform: uppercase;
97
+ color: #aaa;
98
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  .topbar {
100
  height: 50px;
101
  background: #0078d4;
 
105
  padding: 0 20px;
106
  font-weight: bold;
107
  }
108
+ .main {
109
+ margin-left: 240px;
110
+ padding: 20px;
111
+ }
112
  .card-grid {
113
  display: grid;
114
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
 
128
  <body>
129
  {user_sidebar}
130
  <div class="topbar">PowerApps Mockup</div>
131
+ <div class="main">{user_content}</div>
 
 
132
  </body>
133
  </html>
134
  """
135
 
136
  # -----------------------
137
+ # Hardcoded Fluent SVG Icons (Home, Recent, Pinned)
138
  # -----------------------
139
+ SVG_FLUENT = {
140
+ "hamburger": """<svg viewBox="0 0 24 24"><path d="M3 6h18v2H3V6zm0 5h18v2H3v-2zm0 5h18v2H3v-2z"/></svg>""",
141
+ "home": """<svg viewBox="0 0 24 24"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/></svg>""",
142
+ "recent": """<svg viewBox="0 0 24 24"><path d="M12 8v5l4 2 .7-1.2-3.2-1.8V8h-1.5zm0-6a10 10 0 00-10 10H0l4 4 4-4H5a7 7 0 117 7 7 7 0 01-7-7H3a9 9 0 109-9z"/></svg>""",
143
+ "pinned": """<svg viewBox="0 0 24 24"><path d="M14 2v2l2 2v3l2 2v2H6v-2l2-2V6l2-2V2h4zm-1 13v7h-2v-7h2z"/></svg>""",
144
+ }
145
 
146
+ # -----------------------
147
+ # Function: Generate Sidebar (with Active State + Sections)
148
+ # -----------------------
149
+ def generate_sidebar(user_prompt):
150
+ sidebar_html = "<div class='sidebar'>"
151
  sidebar_html += f"<div class='sidebar-header'>{SVG_FLUENT['hamburger']} App Title</div>"
152
 
153
+ # Default hardcoded items
154
+ defaults = [("home", "Home"), ("recent", "Recent"), ("pinned", "Pinned")]
155
+ for key, label in defaults:
156
+ icon = SVG_FLUENT[key]
157
+ sidebar_html += f"<div class='sidebar-item'>{icon}<span>{label}</span></div>"
158
 
159
  sidebar_html += "<div class='sidebar-section'>My Work</div>"
160
 
161
+ # Ask Gemini for structured sidebar with sections and active item
162
+ prompt = (
163
+ "You are a Microsoft PowerApps UI designer. "
164
+ "Based on the following user requirement, generate a complete sidebar structure under 'My Work'. "
165
+ "Include section headings (like MetaData, Templates, Settings, etc.) "
166
+ "and under each section, list items in this format:\n\n"
167
+ "Section Name:\n"
168
+ " Label | <svg ...>\n"
169
+ " Label | <svg ...>\n"
170
+ "\nAdditionally, mark one item as active by adding [ACTIVE] after its label if it represents the main context "
171
+ "(e.g., if the user says 'approval dashboard', mark 'Approvals' as active). "
172
+ "Return ONLY this structured plain text output with inline SVGs and [ACTIVE] tag if applicable."
173
  )
174
 
175
  try:
176
+ response = model.generate_content(prompt + "\n\n" + user_prompt)
177
+ text = response.text.strip()
178
+ except Exception:
179
+ text = """MetaData:
180
+ Document Type | <svg viewBox='0 0 24 24'><path d='M3 13h8V3H3v10z'/></svg>
181
+ Division | <svg viewBox='0 0 24 24'><path d='M12 2L1 21h22L12 2z'/></svg>
182
+ Templates:
183
+ Email Templates [ACTIVE] | <svg viewBox='0 0 24 24'><path d='M4 4h16v16H4V4z'/></svg>
184
+ Settings:
185
+ QMS Settings | <svg viewBox='0 0 24 24'><path d='M19 3H5v18h14V3z'/></svg>"""
186
+
187
+ # Parse structured Gemini output
188
+ current_section = None
189
+ for line in text.splitlines():
190
+ if not line.strip():
191
+ continue
192
+ if not line.startswith(" ") and ":" in line:
193
+ section = line.split(":")[0].strip()
194
+ sidebar_html += f"<div class='sidebar-section'>{section}</div>"
195
+ current_section = section
196
+ elif "|" in line:
197
+ match = re.match(r"\s*(.+?)\s*\|\s*(<svg.*</svg>)", line.strip())
198
+ if match:
199
+ label_raw, svg = match.groups()
200
+ is_active = "[ACTIVE]" in label_raw
201
+ label = label_raw.replace("[ACTIVE]", "").strip()
202
+ active_class = "active" if is_active else ""
203
+ sidebar_html += f"<div class='sidebar-item {active_class}'>{svg}<span>{label}</span></div>"
204
 
205
  sidebar_html += "</div>"
206
  return sidebar_html
 
212
  system_prompt = (
213
  "You are a Power Apps mockup generator. "
214
  "Generate ONLY the inner HTML for the main screen content. "
215
+ "Do NOT include <html>, <head>, or <body>."
216
  )
 
217
  try:
218
  response = model.generate_content(system_prompt + "\n" + user_prompt)
219
  user_html = response.text.strip()
 
 
220
  if user_html.startswith("```"):
221
+ user_html = user_html.split("```")[1].replace("html", "").strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
222
 
223
  sidebar_html = generate_sidebar(user_prompt)
 
224
  full_html = BASE_TEMPLATE.replace("{user_sidebar}", sidebar_html).replace("{user_content}", user_html)
225
 
226
+ uid = str(uuid.uuid4())
227
+ html_path, img_path = f"mockup_{uid}.html", f"mockup_{uid}.png"
228
+ with open(html_path, "w", encoding="utf-8") as f:
229
  f.write(full_html)
230
 
231
+ hti.screenshot(html_file=html_path, save_as=img_path)
232
+ return img_path
233
 
234
  except Exception as e:
235
  return f"Error: {e}"
 
238
  # Gradio UI
239
  # -----------------------
240
  with gr.Blocks() as demo:
241
+ gr.Markdown("## ⚡ Power Apps Mockup Generator (Dynamic Sections + Model Icons + Active Item)")
242
  user_input = gr.Textbox(
243
  label="Describe your Power Apps mockup screen",
244
  lines=4,
245
+ placeholder="Example: An approval dashboard showing requests, reports, and metadata sections"
246
  )
247
  generate_btn = gr.Button("Generate Mockup")
248
  image_output = gr.Image(label="Mockup Preview")