dina1 commited on
Commit
29603f8
·
verified ·
1 Parent(s): 6faee08

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +254 -129
app.py CHANGED
@@ -16,7 +16,69 @@ model = genai.GenerativeModel("gemini-2.0-flash")
16
  hti = Html2Image(browser_executable="/usr/bin/chromium")
17
 
18
  # -----------------------
19
- # Base PowerApps Layout (CSS + Sidebar + Topbar + Row Selection JS)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  # -----------------------
21
  BASE_TEMPLATE = """
22
  <!DOCTYPE html>
@@ -28,138 +90,164 @@ BASE_TEMPLATE = """
28
  <style>
29
  body { margin:0; font-family:Segoe UI, sans-serif; display:flex; height:100vh; background:#f9f9f9; }
30
  .sidebar {
31
- width:220px; background:#f4f4f4; padding:15px; border-right:1px solid #ddd;
 
32
  }
33
  .sidebar h3 { margin-top:20px; margin-bottom:10px; font-size:13px; color:#666; font-weight:bold; text-transform:uppercase; }
34
  .sidebar ul { list-style:none; padding:0; margin:0; }
35
- .sidebar li {
36
- margin:10px 0; color:#333; cursor:pointer; display:flex; align-items:center; gap:10px;
37
- padding:5px 8px; border-radius:6px;
38
  }
39
  .sidebar li:hover { background:#e6e6e6; }
40
  .main { flex:1; display:flex; flex-direction:column; }
41
- .content {
42
- flex:1; padding:25px; overflow:auto; background:white;
 
 
 
 
43
  }
 
 
 
 
 
 
 
 
 
 
 
44
 
45
- /* Table + Row Selection */
46
- .table-container { border:1px solid #ddd; border-radius:6px; overflow:hidden; box-shadow:0 2px 6px rgba(0,0,0,0.08); margin-top:20px; }
47
  table { width:100%; border-collapse:collapse; font-size:14px; }
48
- thead { background:#f4f4f4; text-align:left; font-weight:600; color:#333; }
49
- thead th { padding:10px; border-bottom:1px solid #ddd; vertical-align:top; }
50
  tbody tr { border-bottom:1px solid #eee; cursor:pointer; }
51
  tbody tr:hover { background:#f9f9f9; }
52
  tbody tr.selected { background:#e5f1fb !important; border-left:4px solid #0078d4; }
53
- td { padding:10px; color:#444; }
54
- .status { padding:4px 8px; border-radius:12px; font-size:12px; font-weight:600; display:inline-block; }
55
  .status.active { background:#dff7df; color:#107c41; }
56
  .status.pending { background:#fff3cd; color:#856404; }
57
  .status.closed { background:#f8d7da; color:#721c24; }
58
 
59
- /* Column header filters */
60
- .th-filter { display:flex; flex-direction:column; align-items:flex-start; gap:4px; }
61
- .th-filter select {
62
- width:100%; font-size:12px; padding:3px 6px; border:1px solid #ccc; border-radius:4px; background:white;
63
- }
64
 
65
- /* Table footer */
66
- .table-footer { display:flex; justify-content:space-between; align-items:center; padding:10px; background:#fafafa; border-top:1px solid #ddd; font-size:13px; color:#333; }
67
- .pagination { display:flex; align-items:center; gap:6px; }
68
- .page-btn { padding:4px 8px; border:1px solid #ccc; background:white; border-radius:4px; cursor:pointer; font-size:12px; }
69
- .page-btn:hover { background:#f1f1f1; }
70
- .page-btn.active { background:#0078d4; color:white; border-color:#0078d4; }
71
- .table-filter { display:flex; align-items:center; gap:8px; }
72
- .table-filter input { padding:6px 8px; border:1px solid #ccc; border-radius:4px; font-size:13px; }
73
 
74
  /* Chart styles */
75
  .chart-container { margin:20px 0; padding:16px; border:1px solid #ddd; border-radius:6px; background:#fff; }
76
- .chart-title { font-weight:bold; margin-bottom:12px; }
77
-
78
- /* Bar chart */
79
- .bar-chart { display:flex; align-items:flex-end; gap:12px; height:200px; border-left:2px solid #ccc; border-bottom:2px solid #ccc; padding:10px; background:linear-gradient(to top,#fafafa,#fff); }
80
- .bar { flex:1; min-height:20px; border-radius:4px 4px 0 0; position:relative; display:flex; align-items:flex-end; justify-content:center; }
81
- .bar-label { position:absolute; bottom:-20px; font-size:12px; color:#333; }
82
-
83
- /* Line chart */
84
- .line-chart { position:relative; height:180px; background:#fff; border:1px solid #ddd; border-radius:6px; display:flex; align-items:flex-end; justify-content:space-around; padding:0 10px 30px 10px; }
85
- .line-point { position:relative; width:12px; height:12px; background:#0078d4; border-radius:50%; z-index:2; }
86
- .line-label { position:absolute; bottom:-22px; left:50%; transform:translateX(-50%); font-size:12px; color:#555; }
87
- .line-path { position:absolute; bottom:30px; left:0; width:100%; height:120px; pointer-events:none; }
88
- .line-path svg { width:100%; height:100%; }
89
  .line-path polyline { fill:none; stroke:#0078d4; stroke-width:2; }
90
-
91
- /* Pie chart */
92
  .pie-chart { width:180px; height:180px; border-radius:50%; position:relative; overflow:hidden; margin:auto; }
93
- .slice { position:absolute; width:100%; height:100%; clip-path:polygon(50% 50%, 100% 50%, 100% 0, 50% 0); }
94
-
95
- /* Stacked bar chart */
96
- .stacked-bar-chart { margin:20px 0; }
97
  .stacked-bar { display:flex; height:24px; border-radius:4px; overflow:hidden; margin-bottom:8px; }
98
  .stacked-bar-segment { height:100%; }
99
-
100
- /* Donut chart */
101
  .donut-chart { width:180px; height:180px; border-radius:50%; border:20px solid #0078d4; position:relative; margin:auto; }
102
  .donut-label { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); font-size:18px; font-weight:bold; }
103
-
104
- /* Legends */
105
- .legend { margin-top:10px; display:flex; gap:10px; flex-wrap:wrap; }
106
- .legend-item { display:flex; align-items:center; gap:6px; font-size:12px; }
107
  .legend-color { width:12px; height:12px; display:inline-block; border-radius:3px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  </style>
109
  </head>
110
  <body>
111
  <div class="sidebar">
112
- <ul><li><i class="fas fa-bars"></i> Menu</li></ul>
113
  <h3>Home</h3>
114
  <ul>
115
  <li><i class="fas fa-home"></i> Home</li>
116
- <li><i class="fas fa-clock"></i> Recent <i class="fas fa-chevron-down" style="margin-left:auto"></i></li>
117
- <li><i class="fas fa-thumbtack"></i> Pinned <i class="fas fa-chevron-down" style="margin-left:auto"></i></li>
118
  </ul>
119
- <h3>My Works</h3>
120
  <ul>
121
  <li><i class="fas fa-chart-line"></i> Dashboard</li>
122
  <li><i class="fas fa-tasks"></i> Activities</li>
123
  <li><i class="fas fa-plus-circle"></i> Initiate Request</li>
124
  </ul>
125
  <h3>Requests</h3>
126
- <ul><li><i class="fas fa-folder-open"></i> All Requests</li></ul>
 
 
127
  <h3>User Profile</h3>
128
  <ul>
129
  <li><i class="fas fa-user"></i> My Profile</li>
130
- <li><i class="fas fa-bell"></i> My Notifications</li>
131
- <li><i class="fas fa-comments"></i> My Comments</li>
132
  </ul>
133
  </div>
134
 
135
  <div class="main">
136
- <header class="topbar" style="display:flex;align-items:center;justify-content:space-between;
137
- background:#0f1724;color:#e6eef6;padding:10px 14px;gap:12px;">
138
- <div style="display:flex;align-items:center;gap:12px;">
139
- <button style="background:transparent;border:0;cursor:pointer;">
140
- <i class="fas fa-bars" style="font-size:18px;color:white;"></i>
141
- </button>
142
- <div style="display:flex;align-items:center;gap:10px;">
143
- <div style="width:32px;height:32px;background:#0078d4;color:white;
144
- display:flex;align-items:center;justify-content:center;
145
- font-weight:bold;border-radius:6px;">PA</div>
146
- <div>
147
- <div style="font-weight:600;font-size:15px;">PowerApps Mockup</div>
148
- <div style="font-size:12px;color:#9aa6b2;">Canvas App • Demo</div>
149
- </div>
150
  </div>
151
  </div>
152
- <div style="flex:1;display:flex;justify-content:center;max-width:500px;">
153
- <input type="search" placeholder="Search records, views, or commands"
154
- style="width:100%;padding:6px 10px;border-radius:6px;border:1px solid #444;background:#1c2333;color:white;">
155
- </div>
156
- <div style="display:flex;align-items:center;gap:12px;">
157
- <i class="fas fa-sync-alt" title="Refresh"></i>
158
- <i class="fas fa-filter" title="Filter"></i>
159
- <i class="fas fa-sort" title="Sort"></i>
160
- <i class="fas fa-plus" title="Add"></i>
161
- <i class="fas fa-bell" title="Notifications"></i>
162
- <i class="fas fa-user-circle" style="font-size:20px;" title="User"></i>
163
  </div>
164
  </header>
165
 
@@ -185,70 +273,107 @@ BASE_TEMPLATE = """
185
  # -----------------------
186
  # Function: Generate Mockup Image
187
  # -----------------------
188
- def generate_mockup(user_prompt):
 
 
 
 
189
  system_prompt = (
190
  "You are a Power Apps mockup generator. "
191
- "Generate ONLY the inner HTML for the main screen content. "
192
-
193
- "For tables: use <div class='table-container'> with <table>. "
194
- "Each <th> must contain <div class='th-filter'> with the column label and a <select> dropdown. "
195
- "Always include <div class='table-footer'> with pagination + search filter. "
196
- "Use status chips: <span class='status active'>Active</span>, etc. "
197
-
198
- "For forms: use <div class='form-group'> with <label> and inputs, "
199
- "buttons with class='btn btn-primary' or 'btn btn-secondary'. "
200
-
201
- "For dashboards: use <div class='card-grid'> with <div class='card'> containing "
202
- "icon, title, value, and optional subtitle. "
203
-
204
- # Strengthened chart rules
205
- "Bar chart: <div class='bar-chart'> with <div class='bar' style='height:X%; background:#0078d4;'><div class='bar-label'>Label</div></div>. "
206
- "X must be between 20% and 100%. At least 3 bars. "
207
-
208
- "Line chart: <div class='line-chart'> with multiple <div class='line-point' style='bottom:Ypx;'>. "
209
- "Each must have <div class='line-label'>Month</div>. Connect points with <div class='line-path'><svg><polyline points='x,y ...'></polyline></svg></div>. "
210
- "Y values must be between 40px and 120px. At least 3 points. "
211
 
212
- "Pie chart: <div class='pie-chart'> with multiple <div class='slice' style='background:#color; clip-path:polygon(...)'></div>. "
213
- "Always at least 3 slices with different colors. "
 
 
 
 
 
 
214
 
215
- "Stacked bar chart: <div class='stacked-bar-chart'><div class='stacked-bar'><div class='stacked-bar-segment' style='width:X%; background:#color'></div>...</div></div>. "
216
- "Widths must sum to 100%, with at least 2 segments. "
 
217
 
218
- "Donut chart: <div class='donut-chart'><div class='donut-label'>Value</div></div>. "
219
- "Always visible donut ring with background segments. "
 
220
 
221
- "Always add a <div class='legend'> with <div class='legend-item'><span class='legend-color' style='background:#color'></span> Label</div></div>. "
222
- )
 
 
 
 
 
 
 
 
 
223
 
224
- try:
225
- response = model.generate_content(system_prompt + "\n" + user_prompt)
226
- user_html = response.text.strip()
227
- if user_html.startswith("```"):
228
- user_html = user_html.split("```")[1]
229
- user_html = user_html.replace("html", "").strip()
230
  full_html = BASE_TEMPLATE.replace("{user_content}", user_html)
231
 
 
232
  unique_id = str(uuid.uuid4())
233
  html_file, img_file = f"mockup_{unique_id}.html", f"mockup_{unique_id}.png"
234
- with open(html_file, "w", encoding="utf-8") as f: f.write(full_html)
 
 
235
  hti.screenshot(html_file=html_file, save_as=img_file)
236
- return img_file
 
 
237
  except Exception as e:
238
- return f"Error: {e}"
 
239
 
240
  # -----------------------
241
  # Gradio UI
242
  # -----------------------
243
  with gr.Blocks() as demo:
244
- gr.Markdown("## ⚡ Power Apps Mockup Generator (Gemini + Gradio)")
245
- user_input = gr.Textbox(
246
- label="Describe your Power Apps mockup screen",
247
- lines=4,
248
- placeholder="Example: A login form, OR a requests table, OR a dashboard with bar, line, and pie charts"
249
- )
250
- generate_btn = gr.Button("Generate Mockup")
 
 
251
  image_output = gr.Image(label="Mockup Preview")
252
- generate_btn.click(fn=generate_mockup, inputs=user_input, outputs=image_output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
 
254
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
16
  hti = Html2Image(browser_executable="/usr/bin/chromium")
17
 
18
  # -----------------------
19
+ # Default Chart Snippets (Fallbacks)
20
+ # -----------------------
21
+ DEFAULT_BAR_CHART = """
22
+ <div class="chart-container">
23
+ <div class="chart-title">Sample Bar Chart</div>
24
+ <div class="bar-chart">
25
+ <div class="bar" style="height:40%; background:#0078d4;"><div class="bar-label">Jan</div></div>
26
+ <div class="bar" style="height:70%; background:#0078d4;"><div class="bar-label">Feb</div></div>
27
+ <div class="bar" style="height:90%; background:#0078d4;"><div class="bar-label">Mar</div></div>
28
+ </div>
29
+ </div>
30
+ """
31
+
32
+ DEFAULT_LINE_CHART = """
33
+ <div class="chart-container">
34
+ <div class="chart-title">Sample Line Chart</div>
35
+ <div class="line-chart">
36
+ <div class="line-point" style="bottom:40px;"><div class="line-label">Jan</div></div>
37
+ <div class="line-point" style="bottom:80px;"><div class="line-label">Feb</div></div>
38
+ <div class="line-point" style="bottom:60px;"><div class="line-label">Mar</div></div>
39
+ <div class="line-path"><svg><polyline points="10,120 80,60 150,90" /></svg></div>
40
+ </div>
41
+ </div>
42
+ """
43
+
44
+ DEFAULT_PIE_CHART = """
45
+ <div class="chart-container">
46
+ <div class="chart-title">Sample Pie Chart</div>
47
+ <div class="pie-chart">
48
+ <div class="slice" style="background:#0078d4; clip-path:polygon(50% 50%, 100% 50%, 100% 0, 50% 0);"></div>
49
+ <div class="slice" style="background:#ff9900; clip-path:polygon(50% 50%, 100% 0, 50% 0, 0 0);"></div>
50
+ <div class="slice" style="background:#33cc33; clip-path:polygon(50% 50%, 0 0, 0 50%, 50% 50%);"></div>
51
+ </div>
52
+ <div class="legend">
53
+ <div class="legend-item"><span class="legend-color" style="background:#0078d4"></span> A</div>
54
+ <div class="legend-item"><span class="legend-color" style="background:#ff9900"></span> B</div>
55
+ <div class="legend-item"><span class="legend-color" style="background:#33cc33"></span> C</div>
56
+ </div>
57
+ </div>
58
+ """
59
+
60
+ DEFAULT_STACKED_BAR_CHART = """
61
+ <div class="chart-container">
62
+ <div class="chart-title">Sample Stacked Bar</div>
63
+ <div class="stacked-bar">
64
+ <div class="stacked-bar-segment" style="width:40%; background:#0078d4"></div>
65
+ <div class="stacked-bar-segment" style="width:35%; background:#ff9900"></div>
66
+ <div class="stacked-bar-segment" style="width:25%; background:#33cc33"></div>
67
+ </div>
68
+ </div>
69
+ """
70
+
71
+ DEFAULT_DONUT_CHART = """
72
+ <div class="chart-container">
73
+ <div class="chart-title">Sample Donut Chart</div>
74
+ <div class="donut-chart">
75
+ <div class="donut-label">65%</div>
76
+ </div>
77
+ </div>
78
+ """
79
+
80
+ # -----------------------
81
+ # Base PowerApps Layout
82
  # -----------------------
83
  BASE_TEMPLATE = """
84
  <!DOCTYPE html>
 
90
  <style>
91
  body { margin:0; font-family:Segoe UI, sans-serif; display:flex; height:100vh; background:#f9f9f9; }
92
  .sidebar {
93
+ width:240px; background:#f4f4f4; padding:15px; border-right:1px solid #ddd;
94
+ display:flex; flex-direction:column;
95
  }
96
  .sidebar h3 { margin-top:20px; margin-bottom:10px; font-size:13px; color:#666; font-weight:bold; text-transform:uppercase; }
97
  .sidebar ul { list-style:none; padding:0; margin:0; }
98
+ .sidebar li {
99
+ margin:8px 0; color:#333; cursor:pointer; display:flex; align-items:center; gap:10px;
100
+ padding:6px 10px; border-radius:6px; font-size:14px;
101
  }
102
  .sidebar li:hover { background:#e6e6e6; }
103
  .main { flex:1; display:flex; flex-direction:column; }
104
+ .content { flex:1; padding:25px; overflow:auto; background:white; }
105
+
106
+ /* Top bar */
107
+ .topbar {
108
+ display:flex; align-items:center; justify-content:space-between;
109
+ background:#0f1724; color:#e6eef6; padding:10px 14px; gap:12px;
110
  }
111
+ .topbar .left { display:flex; align-items:center; gap:12px; }
112
+ .topbar .logo { width:32px; height:32px; background:#0078d4; color:white;
113
+ display:flex; align-items:center; justify-content:center; font-weight:bold; border-radius:6px; }
114
+ .topbar .app-title { font-weight:600; font-size:15px; }
115
+ .topbar .subtitle { font-size:12px; color:#9aa6b2; }
116
+ .topbar .search { flex:1; display:flex; justify-content:center; max-width:500px; }
117
+ .topbar .search input {
118
+ width:100%; padding:6px 10px; border-radius:6px; border:1px solid #444;
119
+ background:#1c2333; color:white;
120
+ }
121
+ .topbar .right { display:flex; align-items:center; gap:12px; }
122
 
123
+ /* Table + Row Selection + Filters + Footer */
 
124
  table { width:100%; border-collapse:collapse; font-size:14px; }
125
+ thead { background:#f4f4f4; }
126
+ thead th { padding:10px; border-bottom:1px solid #ddd; }
127
  tbody tr { border-bottom:1px solid #eee; cursor:pointer; }
128
  tbody tr:hover { background:#f9f9f9; }
129
  tbody tr.selected { background:#e5f1fb !important; border-left:4px solid #0078d4; }
130
+ td { padding:10px; }
 
131
  .status.active { background:#dff7df; color:#107c41; }
132
  .status.pending { background:#fff3cd; color:#856404; }
133
  .status.closed { background:#f8d7da; color:#721c24; }
134
 
135
+ .th-filter { display:flex; flex-direction:column; gap:4px; }
136
+ .th-filter select { font-size:12px; padding:3px 6px; border:1px solid #ccc; border-radius:4px; }
 
 
 
137
 
138
+ .table-footer { display:flex; justify-content:space-between; align-items:center;
139
+ padding:10px; background:#fafafa; border-top:1px solid #ddd; font-size:13px; }
140
+ .pagination { display:flex; gap:6px; }
141
+ .page-btn { padding:4px 8px; border:1px solid #ccc; background:white; border-radius:4px; cursor:pointer; }
142
+ .page-btn.active { background:#0078d4; color:white; }
 
 
 
143
 
144
  /* Chart styles */
145
  .chart-container { margin:20px 0; padding:16px; border:1px solid #ddd; border-radius:6px; background:#fff; }
146
+ .bar-chart { display:flex; align-items:flex-end; gap:12px; height:200px; border-left:2px solid #ccc; border-bottom:2px solid #ccc; padding:10px; }
147
+ .bar { flex:1; min-height:20px; border-radius:4px 4px 0 0; position:relative; }
148
+ .bar-label { position:absolute; bottom:-20px; font-size:12px; }
149
+ .line-chart { position:relative; height:180px; border:1px solid #ddd; border-radius:6px; display:flex; justify-content:space-around; align-items:flex-end; padding-bottom:30px; }
150
+ .line-point { width:12px; height:12px; background:#0078d4; border-radius:50%; position:relative; }
151
+ .line-label { position:absolute; bottom:-22px; font-size:12px; }
152
+ .line-path { position:absolute; bottom:30px; left:0; width:100%; height:120px; }
 
 
 
 
 
 
153
  .line-path polyline { fill:none; stroke:#0078d4; stroke-width:2; }
 
 
154
  .pie-chart { width:180px; height:180px; border-radius:50%; position:relative; overflow:hidden; margin:auto; }
155
+ .slice { position:absolute; width:100%; height:100%; }
 
 
 
156
  .stacked-bar { display:flex; height:24px; border-radius:4px; overflow:hidden; margin-bottom:8px; }
157
  .stacked-bar-segment { height:100%; }
 
 
158
  .donut-chart { width:180px; height:180px; border-radius:50%; border:20px solid #0078d4; position:relative; margin:auto; }
159
  .donut-label { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); font-size:18px; font-weight:bold; }
160
+ .legend { margin-top:10px; display:flex; gap:10px; flex-wrap:wrap; font-size:12px; }
 
 
 
161
  .legend-color { width:12px; height:12px; display:inline-block; border-radius:3px; }
162
+
163
+ /* KPI Dashboard Cards */
164
+ .kpi-grid {
165
+ display:grid;
166
+ grid-template-columns: repeat(auto-fit, minmax(200px,1fr));
167
+ gap:16px;
168
+ margin:20px 0;
169
+ }
170
+ .kpi-card {
171
+ background:white;
172
+ border:1px solid #ddd;
173
+ border-radius:8px;
174
+ padding:16px;
175
+ display:flex;
176
+ align-items:center;
177
+ gap:12px;
178
+ box-shadow:0 2px 6px rgba(0,0,0,0.05);
179
+ }
180
+ .kpi-icon {
181
+ width:40px; height:40px;
182
+ display:flex; align-items:center; justify-content:center;
183
+ border-radius:8px;
184
+ background:#f3f6fa;
185
+ color:#0078d4;
186
+ font-size:18px;
187
+ }
188
+ .kpi-content { display:flex; flex-direction:column; }
189
+ .kpi-value { font-size:20px; font-weight:600; }
190
+
191
+ /* KPI Value Colors */
192
+ .kpi-value.positive { color:#107c41; }
193
+ .kpi-value.negative { color:#d13438; }
194
+ .kpi-value.neutral { color:#605e5c; }
195
+
196
+ /* KPI Trend Arrows */
197
+ .kpi-trend {
198
+ font-size:14px;
199
+ margin-left:4px;
200
+ font-weight:bold;
201
+ }
202
+ .kpi-value.positive .kpi-trend { color:#107c41; }
203
+ .kpi-value.negative .kpi-trend { color:#d13438; }
204
+ .kpi-value.neutral .kpi-trend { color:#605e5c; }
205
  </style>
206
  </head>
207
  <body>
208
  <div class="sidebar">
 
209
  <h3>Home</h3>
210
  <ul>
211
  <li><i class="fas fa-home"></i> Home</li>
212
+ <li><i class="fas fa-clock"></i> Recent</li>
213
+ <li><i class="fas fa-thumbtack"></i> Pinned</li>
214
  </ul>
215
+ <h3>My Work</h3>
216
  <ul>
217
  <li><i class="fas fa-chart-line"></i> Dashboard</li>
218
  <li><i class="fas fa-tasks"></i> Activities</li>
219
  <li><i class="fas fa-plus-circle"></i> Initiate Request</li>
220
  </ul>
221
  <h3>Requests</h3>
222
+ <ul>
223
+ <li><i class="fas fa-folder-open"></i> All Requests</li>
224
+ </ul>
225
  <h3>User Profile</h3>
226
  <ul>
227
  <li><i class="fas fa-user"></i> My Profile</li>
228
+ <li><i class="fas fa-bell"></i> Notifications</li>
229
+ <li><i class="fas fa-comments"></i> Comments</li>
230
  </ul>
231
  </div>
232
 
233
  <div class="main">
234
+ <header class="topbar">
235
+ <div class="left">
236
+ <i class="fas fa-bars"></i>
237
+ <div class="logo">PA</div>
238
+ <div>
239
+ <div class="app-title">PowerApps Mockup</div>
240
+ <div class="subtitle">Canvas App • Demo</div>
 
 
 
 
 
 
 
241
  </div>
242
  </div>
243
+ <div class="search"><input type="search" placeholder="Search..." /></div>
244
+ <div class="right">
245
+ <i class="fas fa-sync-alt"></i>
246
+ <i class="fas fa-filter"></i>
247
+ <i class="fas fa-sort"></i>
248
+ <i class="fas fa-plus"></i>
249
+ <i class="fas fa-bell"></i>
250
+ <i class="fas fa-user-circle"></i>
 
 
 
251
  </div>
252
  </header>
253
 
 
273
  # -----------------------
274
  # Function: Generate Mockup Image
275
  # -----------------------
276
+ def generate_mockup_html(user_prompt, prev_html=None):
277
+ """
278
+ If prev_html is provided, treat user_prompt as a modification request.
279
+ Otherwise, treat it as fresh generation.
280
+ """
281
  system_prompt = (
282
  "You are a Power Apps mockup generator. "
283
+ "Generate ONLY the inner HTML for the main content area (inside <div class='content'>). "
284
+ "For tables: include filters in headers, footer with pagination + search, and row selection. "
285
+ "For forms: use <label> + <input>, with Save/Cancel buttons. "
286
+ "For dashboards: use KPI cards styled with <div class='kpi-grid'><div class='kpi-card'>...</div></div>. "
287
+ "Each KPI card must include .kpi-icon, .kpi-value, and .kpi-label. "
288
+ "Apply .positive (green), .negative (red), or .neutral (grey) to .kpi-value. "
289
+ "Inside .kpi-value, also include <span class='kpi-trend'>⬆️, ⬇️, or ➖</span>. "
290
+ "For charts: enforce visible bars, points, slices, stacked segments, and donut rings with legends. "
291
+ )
 
 
 
 
 
 
 
 
 
 
 
292
 
293
+ try:
294
+ if prev_html: # Modification mode
295
+ mod_prompt = f"Here is the current HTML:\n{prev_html}\nModify it based on this request:\n{user_prompt}"
296
+ response = model.generate_content(mod_prompt)
297
+ user_html = response.text.strip()
298
+ else: # Fresh generation
299
+ response = model.generate_content(system_prompt + "\n" + user_prompt)
300
+ user_html = response.text.strip()
301
 
302
+ # Strip markdown
303
+ if user_html.startswith("```"):
304
+ user_html = user_html.split("```")[1].replace("html", "").strip()
305
 
306
+ # Remove <html>/<body>
307
+ for tag in ["<html>", "</html>", "<body>", "</body>"]:
308
+ user_html = user_html.replace(tag, "")
309
 
310
+ # Fallbacks
311
+ if any(word in user_prompt.lower() for word in ["bar", "bar chart"]) and "bar-chart" not in user_html:
312
+ user_html += DEFAULT_BAR_CHART
313
+ if "line" in user_prompt.lower() and "line-chart" not in user_html:
314
+ user_html += DEFAULT_LINE_CHART
315
+ if "pie" in user_prompt.lower() and "pie-chart" not in user_html:
316
+ user_html += DEFAULT_PIE_CHART
317
+ if "stacked" in user_prompt.lower() and "stacked-bar" not in user_html:
318
+ user_html += DEFAULT_STACKED_BAR_CHART
319
+ if "donut" in user_prompt.lower() and "donut-chart" not in user_html:
320
+ user_html += DEFAULT_DONUT_CHART
321
 
322
+ # Wrap
 
 
 
 
 
323
  full_html = BASE_TEMPLATE.replace("{user_content}", user_html)
324
 
325
+ # Save
326
  unique_id = str(uuid.uuid4())
327
  html_file, img_file = f"mockup_{unique_id}.html", f"mockup_{unique_id}.png"
328
+ with open(html_file, "w", encoding="utf-8") as f:
329
+ f.write(full_html)
330
+
331
  hti.screenshot(html_file=html_file, save_as=img_file)
332
+ os.remove(html_file)
333
+
334
+ return user_html, img_file
335
  except Exception as e:
336
+ print("Error:", e)
337
+ return prev_html, None
338
 
339
  # -----------------------
340
  # Gradio UI
341
  # -----------------------
342
  with gr.Blocks() as demo:
343
+ gr.Markdown("## ⚡ Power Apps Mockup Generator with Real-time Modifications")
344
+
345
+ state_html = gr.State() # stores current HTML
346
+ state_img = gr.State() # stores current image path
347
+
348
+ with gr.Row():
349
+ user_input = gr.Textbox(label="Describe your Power Apps mockup screen", lines=4)
350
+ generate_btn = gr.Button("Generate Mockup")
351
+
352
  image_output = gr.Image(label="Mockup Preview")
353
+
354
+ # Ask if user wants to modify
355
+ modify_prompt = gr.Textbox(label="Enter modification", visible=False, lines=2)
356
+ modify_btn = gr.Button("Apply Modification", visible=False)
357
+
358
+ # --- Actions ---
359
+ def generate_action(prompt):
360
+ html, img = generate_mockup_html(prompt)
361
+ return html, img, img, gr.update(visible=True), gr.update(visible=True)
362
+
363
+ def modify_action(mod_request, prev_html):
364
+ html, img = generate_mockup_html(mod_request, prev_html)
365
+ return html, img, img
366
+
367
+ generate_btn.click(
368
+ fn=generate_action,
369
+ inputs=[user_input],
370
+ outputs=[state_html, state_img, image_output, modify_prompt, modify_btn]
371
+ )
372
+
373
+ modify_btn.click(
374
+ fn=modify_action,
375
+ inputs=[modify_prompt, state_html],
376
+ outputs=[state_html, state_img, image_output]
377
+ )
378
 
379
  demo.launch(server_name="0.0.0.0", server_port=7860)