Update app.py
Browse files
app.py
CHANGED
|
@@ -16,78 +16,172 @@ model = genai.GenerativeModel("gemini-2.0-flash")
|
|
| 16 |
hti = Html2Image(browser_executable="/usr/bin/chromium")
|
| 17 |
|
| 18 |
# -----------------------
|
| 19 |
-
#
|
| 20 |
# -----------------------
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 61 |
-
<
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
</div>
|
| 69 |
</div>
|
| 70 |
-
</div>
|
| 71 |
-
"""
|
| 72 |
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
</
|
| 83 |
-
</
|
|
|
|
| 84 |
"""
|
| 85 |
|
| 86 |
-
# -----------------------
|
| 87 |
-
# Base PowerApps Layout (CSS + Sidebar + Topbar + Row Selection JS)
|
| 88 |
-
# -----------------------
|
| 89 |
-
BASE_TEMPLATE = """ ... (same as before, with chart CSS, table CSS, row selection, sidebar, topbar) ... """
|
| 90 |
-
|
| 91 |
# -----------------------
|
| 92 |
# Function: Generate Mockup Image
|
| 93 |
# -----------------------
|
|
@@ -133,19 +227,6 @@ def generate_mockup(user_prompt):
|
|
| 133 |
if user_html.startswith("```"):
|
| 134 |
user_html = user_html.split("```")[1]
|
| 135 |
user_html = user_html.replace("html", "").strip()
|
| 136 |
-
|
| 137 |
-
# --- Fallback injection ---
|
| 138 |
-
if "bar chart" in user_prompt.lower() and "bar-chart" not in user_html:
|
| 139 |
-
user_html += DEFAULT_BAR_CHART
|
| 140 |
-
if "line chart" in user_prompt.lower() and "line-chart" not in user_html:
|
| 141 |
-
user_html += DEFAULT_LINE_CHART
|
| 142 |
-
if "pie chart" in user_prompt.lower() and "pie-chart" not in user_html:
|
| 143 |
-
user_html += DEFAULT_PIE_CHART
|
| 144 |
-
if "stacked bar" in user_prompt.lower() and "stacked-bar-chart" not in user_html:
|
| 145 |
-
user_html += DEFAULT_STACKED_BAR_CHART
|
| 146 |
-
if "donut" in user_prompt.lower() and "donut-chart" not in user_html:
|
| 147 |
-
user_html += DEFAULT_DONUT_CHART
|
| 148 |
-
|
| 149 |
full_html = BASE_TEMPLATE.replace("{user_content}", user_html)
|
| 150 |
|
| 151 |
unique_id = str(uuid.uuid4())
|
|
|
|
| 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>
|
| 23 |
+
<html lang="en">
|
| 24 |
+
<head>
|
| 25 |
+
<meta charset="UTF-8">
|
| 26 |
+
<title>Power Apps Mockup</title>
|
| 27 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 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 |
+
|
| 166 |
+
<div class="content">
|
| 167 |
+
{user_content}
|
| 168 |
</div>
|
| 169 |
</div>
|
|
|
|
|
|
|
| 170 |
|
| 171 |
+
<script>
|
| 172 |
+
document.addEventListener('click', function(e) {
|
| 173 |
+
if(e.target.closest('tbody tr')) {
|
| 174 |
+
let row = e.target.closest('tbody tr');
|
| 175 |
+
let tbody = row.closest('tbody');
|
| 176 |
+
tbody.querySelectorAll('tr').forEach(r => r.classList.remove('selected'));
|
| 177 |
+
row.classList.add('selected');
|
| 178 |
+
}
|
| 179 |
+
});
|
| 180 |
+
</script>
|
| 181 |
+
</body>
|
| 182 |
+
</html>
|
| 183 |
"""
|
| 184 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
# -----------------------
|
| 186 |
# Function: Generate Mockup Image
|
| 187 |
# -----------------------
|
|
|
|
| 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())
|