Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -43,12 +43,18 @@ def image_to_base64(image_path):
|
|
| 43 |
with open(image_path, "rb") as img_file:
|
| 44 |
return base64.b64encode(img_file.read()).decode('utf-8')
|
| 45 |
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
# ==========================================
|
| 51 |
-
# 3. BACKEND LOGIC
|
| 52 |
# ==========================================
|
| 53 |
def get_embedding_via_api(text):
|
| 54 |
if not client: raise ValueError("HF_TOKEN missing")
|
|
@@ -71,9 +77,10 @@ def find_similar_recipes_list(query_text):
|
|
| 71 |
desc = str(row['Raw_Output'])
|
| 72 |
score_display = f"{score:.0%}"
|
| 73 |
|
|
|
|
| 74 |
card_content = (
|
| 75 |
-
f"
|
| 76 |
-
f"
|
| 77 |
f"<div class='sim-scroll'>{desc}</div>"
|
| 78 |
)
|
| 79 |
results_list.append(card_content)
|
|
@@ -109,311 +116,301 @@ def ui_update_pipeline(image_path):
|
|
| 109 |
return f"Error: {e}", "Error", gr.update(), gr.update(), "", gr.update(), ""
|
| 110 |
|
| 111 |
# ==========================================
|
| 112 |
-
# 4.
|
| 113 |
# ==========================================
|
| 114 |
-
theme
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
font=[
|
| 119 |
)
|
| 120 |
|
| 121 |
-
|
| 122 |
-
body
|
|
|
|
| 123 |
|
| 124 |
-
/*
|
| 125 |
-
.
|
| 126 |
-
background:
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
display: flex;
|
| 131 |
align-items: center;
|
|
|
|
|
|
|
|
|
|
| 132 |
justify-content: space-between;
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
}
|
| 139 |
|
| 140 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
|
| 142 |
-
.
|
| 143 |
-
.
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
}
|
| 151 |
-
.
|
| 152 |
|
| 153 |
-
/* Sidebar
|
| 154 |
.nav-btn {
|
| 155 |
text-align: left !important;
|
| 156 |
justify-content: flex-start !important;
|
| 157 |
background: transparent !important;
|
| 158 |
border: none !important;
|
| 159 |
box-shadow: none !important;
|
| 160 |
-
color: #
|
| 161 |
font-weight: 600 !important;
|
| 162 |
-
font-size:
|
| 163 |
-
padding:
|
| 164 |
-
border-radius:
|
| 165 |
-
|
| 166 |
}
|
| 167 |
-
.nav-btn:hover { background-color: #e4e6eb !important;
|
| 168 |
-
.nav-btn.selected {
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
}
|
|
|
|
|
|
|
| 173 |
|
| 174 |
-
/*
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
.content-card {
|
| 179 |
-
background-color: #ffffff !important;
|
| 180 |
-
border-radius: 12px;
|
| 181 |
-
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
| 182 |
-
border: 1px solid #ddd;
|
| 183 |
-
padding: 20px;
|
| 184 |
-
margin-bottom: 7px !important;
|
| 185 |
-
width: 100%;
|
| 186 |
-
display: block;
|
| 187 |
}
|
|
|
|
|
|
|
| 188 |
|
| 189 |
-
/*
|
| 190 |
-
.
|
| 191 |
-
|
| 192 |
-
border-radius: 10px;
|
| 193 |
-
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
| 194 |
-
border: 1px solid #ddd;
|
| 195 |
-
padding: 0;
|
| 196 |
-
overflow: hidden;
|
| 197 |
-
height: 100%;
|
| 198 |
-
transition: box-shadow 0.2s;
|
| 199 |
-
display: flex;
|
| 200 |
-
flex-direction: column;
|
| 201 |
-
}
|
| 202 |
-
.saved-card-container:hover { box-shadow: 0 8px 16px rgba(0,0,0,0.1); }
|
| 203 |
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
height: 100px;
|
| 207 |
-
display: flex;
|
| 208 |
-
align-items: center;
|
| 209 |
-
justify-content: center;
|
| 210 |
-
font-size: 40px;
|
| 211 |
-
color: #888;
|
| 212 |
-
}
|
| 213 |
-
|
| 214 |
-
.saved-body { padding: 15px; flex-grow: 1; }
|
| 215 |
-
.saved-title { font-weight: 700; font-size: 16px; margin-bottom: 5px; color: #050505; line-height: 1.3; }
|
| 216 |
-
.saved-meta { font-size: 12px; color: #65676b; margin-bottom: 10px; }
|
| 217 |
-
|
| 218 |
-
/* Right Sidebar Buttons (Saved Recipes List) */
|
| 219 |
-
.sidebar-saved-btn {
|
| 220 |
-
text-align: left !important;
|
| 221 |
-
justify-content: flex-start !important;
|
| 222 |
-
background: white !important;
|
| 223 |
-
border: 1px solid #e0e0e0 !important;
|
| 224 |
-
box-shadow: 0 1px 2px rgba(0,0,0,0.05) !important;
|
| 225 |
-
margin-bottom: 8px !important;
|
| 226 |
-
color: #333 !important;
|
| 227 |
-
padding: 8px 12px !important;
|
| 228 |
-
font-size: 14px !important;
|
| 229 |
-
font-weight: 500 !important;
|
| 230 |
-
}
|
| 231 |
-
.sidebar-saved-btn:hover {
|
| 232 |
-
background: #f0f2f5 !important;
|
| 233 |
-
border-color: #d0d0d0 !important;
|
| 234 |
-
color: #1877f2 !important;
|
| 235 |
-
}
|
| 236 |
-
|
| 237 |
-
.sim-card {
|
| 238 |
-
background: #fff;
|
| 239 |
-
border: 1px solid #eee;
|
| 240 |
-
border-radius: 8px;
|
| 241 |
-
padding: 15px;
|
| 242 |
-
height: 100%;
|
| 243 |
-
border-top: 4px solid #1877f2;
|
| 244 |
-
display: flex; flex-direction: column; justify-content: space-between;
|
| 245 |
-
}
|
| 246 |
-
.sim-scroll { height: 400px; overflow-y: auto; margin-bottom: 10px; padding-right: 5px; font-size: 14px; color: #4b4f56; }
|
| 247 |
-
.gap-fix { gap: 25px !important; }
|
| 248 |
-
.gradio-examples { display: flex; justify-content: center; width: 100%; }
|
| 249 |
-
button.gallery-item { transition: transform 0.2s ease, box-shadow 0.2s ease !important; z-index: 1; }
|
| 250 |
-
button.gallery-item:hover { transform: scale(2.5) !important; z-index: 1000 !important; box-shadow: 0 10px 25px rgba(0,0,0,0.3) !important; border: 2px solid white !important; border-radius: 8px !important; }
|
| 251 |
"""
|
| 252 |
|
| 253 |
# ==========================================
|
| 254 |
# 5. LAYOUT CONSTRUCTION
|
| 255 |
# ==========================================
|
| 256 |
-
with gr.Blocks(title="Legacy Kitchen") as demo:
|
| 257 |
|
| 258 |
-
# ---
|
| 259 |
gr.HTML(f"""
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
</div>
|
|
|
|
| 270 |
""")
|
| 271 |
|
| 272 |
-
|
|
|
|
| 273 |
|
| 274 |
-
#
|
| 275 |
-
with gr.Column(
|
| 276 |
gr.HTML(f"""
|
| 277 |
-
<div
|
| 278 |
-
<img src="data:image/jpeg;base64,{profile_b64}" style="width:
|
| 279 |
-
<
|
| 280 |
</div>
|
| 281 |
""")
|
| 282 |
-
gr.
|
|
|
|
|
|
|
|
|
|
| 283 |
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
nav_about = gr.Button("ℹ️ About", elem_classes=["nav-btn"])
|
| 288 |
|
| 289 |
-
#
|
| 290 |
-
with gr.Column(
|
| 291 |
|
| 292 |
-
#
|
| 293 |
with gr.Group(visible=True) as digitalizer_view:
|
| 294 |
-
with gr.Row(elem_classes=["gap-fix"]):
|
| 295 |
-
with gr.Column(scale=1):
|
| 296 |
-
with gr.Group(elem_classes=["content-card"]):
|
| 297 |
-
input_img = gr.Image(type="filepath", label="Upload", height=300)
|
| 298 |
-
magic_btn = gr.Button("✨ Convert to Digital", variant="primary", size="lg")
|
| 299 |
-
gr.Examples(
|
| 300 |
-
examples=[
|
| 301 |
-
["quick_tries_images/applecrisp.jpg"],
|
| 302 |
-
["quick_tries_images/meatballs recipe.jpg"],
|
| 303 |
-
["quick_tries_images/chocolateballs.png"],
|
| 304 |
-
["quick_tries_images/Apple Dapple (aka Fresh Apple Cake).jfif"]
|
| 305 |
-
],
|
| 306 |
-
inputs=input_img,
|
| 307 |
-
label="Or try these examples:",
|
| 308 |
-
cache_examples=False
|
| 309 |
-
)
|
| 310 |
-
with gr.Column(scale=1):
|
| 311 |
-
with gr.Group(elem_classes=["content-card"]):
|
| 312 |
-
out_text = gr.Textbox(label="Result", value="Here your digitalized recipe will be presented", lines=20, interactive=False, show_label=False)
|
| 313 |
-
|
| 314 |
-
gr.HTML("<div style='height: 35px;'></div>")
|
| 315 |
-
gr.Markdown("### 3. Similar Recipes from Database")
|
| 316 |
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
gr.Button("👍 Like", size="sm", variant="secondary")
|
| 322 |
-
gr.Button("↗️ Share", size="sm", variant="secondary")
|
| 323 |
|
| 324 |
-
with gr.
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
|
| 330 |
-
with gr.
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
|
| 336 |
magic_btn.click(ui_update_pipeline, input_img, [out_text, sim1, c1_btns, c2_box, sim2, c3_box, sim3])
|
| 337 |
|
| 338 |
-
#
|
| 339 |
-
with gr.Column(visible=False
|
| 340 |
-
if
|
| 341 |
feed_samples = df_recipes.sample(10)
|
| 342 |
for index, row in feed_samples.iterrows():
|
| 343 |
user_name = random.choice(["Grandma Rose", "Chef Mike", "Sarah J."])
|
| 344 |
emoji = random.choice(["🥘", "🥗", "🍰", "🌮"])
|
| 345 |
-
post_time = random.choice(["2h", "3h", "
|
| 346 |
|
| 347 |
-
with gr.Group(elem_classes=["
|
|
|
|
| 348 |
gr.HTML(f"""
|
| 349 |
-
<div
|
| 350 |
-
<div style="
|
| 351 |
-
<div
|
|
|
|
|
|
|
|
|
|
| 352 |
</div>
|
| 353 |
""")
|
| 354 |
-
gr.Markdown(f"
|
| 355 |
-
gr.Markdown(f"{str(row['Raw_Output'])[:
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
gr.Button("
|
|
|
|
|
|
|
| 360 |
else:
|
| 361 |
gr.Markdown("⚠️ Database is empty.")
|
| 362 |
|
| 363 |
-
#
|
| 364 |
with gr.Column(visible=False) as saved_view:
|
| 365 |
-
gr.Markdown("### 🔖 Saved
|
| 366 |
-
|
| 367 |
if not df_recipes.empty:
|
| 368 |
-
saved_batch = df_recipes.head(
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
gr.HTML(
|
| 376 |
-
|
| 377 |
-
gr.HTML(f"<div class='saved-title'>{row['Title']}</div>")
|
| 378 |
-
gr.HTML(f"<div class='saved-meta'>Saved to Collection</div>")
|
| 379 |
-
gr.Button("View", size="sm", variant="secondary")
|
| 380 |
-
else:
|
| 381 |
-
gr.Markdown("Database empty.")
|
| 382 |
|
| 383 |
-
#
|
| 384 |
with gr.Group(visible=False) as about_view:
|
| 385 |
-
with gr.Group(elem_classes=["
|
| 386 |
-
gr.Markdown(""
|
| 387 |
-
|
| 388 |
-
The goal of this project is to develop an app that takes a scanned image of a handwritten recipe as input, generates text using a VLM, and based on the extracted text, suggests 3 similar recipes from a 10K dataset of synthetic recipes. Our app will bridge the gap between analog culinary heritage and digital discovery.
|
| 389 |
-
|
| 390 |
-
### About Us
|
| 391 |
-
This app was developed by **Shahar Firshtman** and **Lior Feinstein**, 2nd year students for Economics and data science.
|
| 392 |
-
""")
|
| 393 |
gr.HTML(f"""
|
| 394 |
<div style="margin-top: 20px;">
|
| 395 |
-
<
|
| 396 |
-
<img src="data:image/jpeg;base64,{process_b64}" style="width: 100%; height: auto; border-radius: 8px; border: 1px solid #ddd;">
|
| 397 |
</div>
|
| 398 |
""")
|
| 399 |
|
| 400 |
-
#
|
| 401 |
-
with gr.Column(
|
| 402 |
-
|
| 403 |
-
gr.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 404 |
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 414 |
|
| 415 |
# ==========================================
|
| 416 |
-
# 6. JAVASCRIPT LOGIC
|
| 417 |
# ==========================================
|
| 418 |
def go_digi():
|
| 419 |
return (
|
|
@@ -442,10 +439,6 @@ with gr.Blocks(title="Legacy Kitchen") as demo:
|
|
| 442 |
nav_feed.click(go_feed, None, outputs_ui)
|
| 443 |
nav_saved.click(go_saved, None, outputs_ui)
|
| 444 |
nav_about.click(go_about, None, outputs_ui)
|
| 445 |
-
|
| 446 |
-
# NEW: Bind right sidebar buttons to go_saved function
|
| 447 |
-
for btn in sidebar_btns:
|
| 448 |
-
btn.click(go_saved, None, outputs_ui)
|
| 449 |
|
| 450 |
if __name__ == "__main__":
|
| 451 |
-
demo.launch(
|
|
|
|
| 43 |
with open(image_path, "rb") as img_file:
|
| 44 |
return base64.b64encode(img_file.read()).decode('utf-8')
|
| 45 |
|
| 46 |
+
# Using placeholders if files don't exist to prevent crash
|
| 47 |
+
try:
|
| 48 |
+
logo_b64 = image_to_base64("logo.jpg")
|
| 49 |
+
profile_b64 = image_to_base64("232px-Tv_the_muppet_show_bein_green.jpg")
|
| 50 |
+
process_b64 = image_to_base64("preview of process.jpg")
|
| 51 |
+
except:
|
| 52 |
+
logo_b64 = ""
|
| 53 |
+
profile_b64 = ""
|
| 54 |
+
process_b64 = ""
|
| 55 |
|
| 56 |
# ==========================================
|
| 57 |
+
# 3. BACKEND LOGIC (UNCHANGED)
|
| 58 |
# ==========================================
|
| 59 |
def get_embedding_via_api(text):
|
| 60 |
if not client: raise ValueError("HF_TOKEN missing")
|
|
|
|
| 77 |
desc = str(row['Raw_Output'])
|
| 78 |
score_display = f"{score:.0%}"
|
| 79 |
|
| 80 |
+
# HTML styled as a "Suggested Post"
|
| 81 |
card_content = (
|
| 82 |
+
f"<div class='suggested-header'>Suggested for you · {score_display} Match</div>"
|
| 83 |
+
f"### 🥘 {title}\n"
|
| 84 |
f"<div class='sim-scroll'>{desc}</div>"
|
| 85 |
)
|
| 86 |
results_list.append(card_content)
|
|
|
|
| 116 |
return f"Error: {e}", "Error", gr.update(), gr.update(), "", gr.update(), ""
|
| 117 |
|
| 118 |
# ==========================================
|
| 119 |
+
# 4. FACEBOOK UI THEME & CSS
|
| 120 |
# ==========================================
|
| 121 |
+
# We override the theme to be neutral so our CSS takes over
|
| 122 |
+
theme = gr.themes.Default(
|
| 123 |
+
primary_hue="blue",
|
| 124 |
+
radius_size="sm",
|
| 125 |
+
font=['Helvetica', 'Arial', 'sans-serif']
|
| 126 |
)
|
| 127 |
|
| 128 |
+
facebook_css = """
|
| 129 |
+
body { background-color: #f0f2f5; margin: 0; padding: 0; }
|
| 130 |
+
.gradio-container { background-color: #f0f2f5 !important; max-width: 100% !important; margin: 0 !important; padding: 0 !important; }
|
| 131 |
|
| 132 |
+
/* 1. STICKY NAVBAR */
|
| 133 |
+
.fb-navbar {
|
| 134 |
+
background: white;
|
| 135 |
+
height: 56px;
|
| 136 |
+
padding: 0 16px;
|
| 137 |
+
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
| 138 |
display: flex;
|
| 139 |
align-items: center;
|
| 140 |
+
position: sticky;
|
| 141 |
+
top: 0;
|
| 142 |
+
z-index: 999;
|
| 143 |
justify-content: space-between;
|
| 144 |
+
}
|
| 145 |
+
.fb-logo {
|
| 146 |
+
color: #1877F2;
|
| 147 |
+
font-size: 28px;
|
| 148 |
+
font-weight: bold;
|
| 149 |
+
letter-spacing: -0.5px;
|
| 150 |
+
margin-right: 20px;
|
| 151 |
+
}
|
| 152 |
+
.fb-search {
|
| 153 |
+
background: #f0f2f5;
|
| 154 |
+
border-radius: 50px;
|
| 155 |
+
padding: 10px 20px;
|
| 156 |
+
color: #65676B;
|
| 157 |
+
width: 250px;
|
| 158 |
+
display: flex;
|
| 159 |
+
align-items: center;
|
| 160 |
}
|
| 161 |
|
| 162 |
+
/* 2. LAYOUT COLUMNS */
|
| 163 |
+
.main-layout { display: flex; justify-content: center; padding-top: 20px; gap: 20px; }
|
| 164 |
+
.col-left { width: 300px; display: none; } /* Hidden on mobile */
|
| 165 |
+
.col-feed { width: 600px; max-width: 100%; }
|
| 166 |
+
.col-right { width: 300px; display: none; }
|
| 167 |
|
| 168 |
+
/* 3. CARD STYLING (The core FB look) */
|
| 169 |
+
.fb-card {
|
| 170 |
+
background: white;
|
| 171 |
+
border-radius: 8px;
|
| 172 |
+
box-shadow: 0 1px 2px rgba(0,0,0,0.2);
|
| 173 |
+
margin-bottom: 15px;
|
| 174 |
+
overflow: hidden;
|
| 175 |
+
padding: 12px 16px;
|
| 176 |
}
|
| 177 |
+
.no-padding { padding: 0 !important; }
|
| 178 |
|
| 179 |
+
/* 4. NAVIGATION BUTTONS (Left Sidebar) */
|
| 180 |
.nav-btn {
|
| 181 |
text-align: left !important;
|
| 182 |
justify-content: flex-start !important;
|
| 183 |
background: transparent !important;
|
| 184 |
border: none !important;
|
| 185 |
box-shadow: none !important;
|
| 186 |
+
color: #050505 !important;
|
| 187 |
font-weight: 600 !important;
|
| 188 |
+
font-size: 15px !important;
|
| 189 |
+
padding: 10px 8px !important;
|
| 190 |
+
border-radius: 8px !important;
|
| 191 |
+
margin-bottom: 5px !important;
|
| 192 |
}
|
| 193 |
+
.nav-btn:hover { background-color: #e4e6eb !important; }
|
| 194 |
+
.nav-btn.selected { background-color: #e7f3ff !important; color: #1877f2 !important; }
|
| 195 |
+
|
| 196 |
+
/* 5. POST HEADER (Avatar + Name) */
|
| 197 |
+
.post-header { display: flex; align-items: center; margin-bottom: 12px; }
|
| 198 |
+
.user-avatar { width: 40px; height: 40px; border-radius: 50%; background: #ddd; margin-right: 10px; object-fit: cover; }
|
| 199 |
+
.post-info { display: flex; flex-direction: column; }
|
| 200 |
+
.post-author { font-weight: 600; color: #050505; font-size: 15px; }
|
| 201 |
+
.post-meta { font-size: 13px; color: #65676B; }
|
| 202 |
+
|
| 203 |
+
/* 6. POST ACTIONS (Like/Comment/Share) */
|
| 204 |
+
.post-actions {
|
| 205 |
+
border-top: 1px solid #ced0d4;
|
| 206 |
+
margin-top: 10px;
|
| 207 |
+
padding-top: 5px;
|
| 208 |
+
display: flex;
|
| 209 |
+
justify-content: space-around;
|
| 210 |
}
|
| 211 |
+
.action-btn { background: transparent !important; color: #65676B !important; box-shadow: none !important; }
|
| 212 |
+
.action-btn:hover { background: #f2f2f2 !important; }
|
| 213 |
|
| 214 |
+
/* 7. RIGHT SIDEBAR (Contacts style) */
|
| 215 |
+
.contact-row {
|
| 216 |
+
display: flex; align-items: center; padding: 8px; border-radius: 8px; cursor: pointer;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
}
|
| 218 |
+
.contact-row:hover { background-color: #e4e6eb; }
|
| 219 |
+
.contact-name { font-weight: 500; font-size: 14px; margin-left: 10px; color: #050505; }
|
| 220 |
|
| 221 |
+
/* UTILS */
|
| 222 |
+
.sim-scroll { height: 200px; overflow-y: auto; font-size: 13px; color: #050505; }
|
| 223 |
+
.suggested-header { font-size: 12px; font-weight: bold; color: #65676B; margin-bottom: 5px; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
|
| 225 |
+
/* Media Queries */
|
| 226 |
+
@media (min-width: 1100px) { .col-left, .col-right { display: block; } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
"""
|
| 228 |
|
| 229 |
# ==========================================
|
| 230 |
# 5. LAYOUT CONSTRUCTION
|
| 231 |
# ==========================================
|
| 232 |
+
with gr.Blocks(title="Legacy Kitchen", css=facebook_css, theme=theme) as demo:
|
| 233 |
|
| 234 |
+
# --- 1. FACEBOOK NAVBAR ---
|
| 235 |
gr.HTML(f"""
|
| 236 |
+
<div class="fb-navbar">
|
| 237 |
+
<div style="display:flex; align-items:center;">
|
| 238 |
+
<div class="fb-logo">facebook</div>
|
| 239 |
+
<div class="fb-search">🔍 Search Legacy Kitchen</div>
|
| 240 |
+
</div>
|
| 241 |
+
<div style="display:flex; gap:10px;">
|
| 242 |
+
<div style="width:40px;height:40px;background:#e4e6eb;border-radius:50%;display:flex;align-items:center;justify-content:center;">➕</div>
|
| 243 |
+
<div style="width:40px;height:40px;background:#e4e6eb;border-radius:50%;display:flex;align-items:center;justify-content:center;">💬</div>
|
| 244 |
+
<img src="data:image/jpeg;base64,{profile_b64}" style="width:40px; height:40px; border-radius:50%; object-fit:cover;">
|
| 245 |
</div>
|
| 246 |
+
</div>
|
| 247 |
""")
|
| 248 |
|
| 249 |
+
# --- 2. MAIN 3-COLUMN LAYOUT ---
|
| 250 |
+
with gr.Row(elem_classes=["main-layout"]):
|
| 251 |
|
| 252 |
+
# === LEFT COLUMN (Sidebar Navigation) ===
|
| 253 |
+
with gr.Column(elem_classes=["col-left"]):
|
| 254 |
gr.HTML(f"""
|
| 255 |
+
<div class="contact-row">
|
| 256 |
+
<img src="data:image/jpeg;base64,{profile_b64}" style="width:36px; height:36px; border-radius:50%;">
|
| 257 |
+
<div class="contact-name">Welcome User</div>
|
| 258 |
</div>
|
| 259 |
""")
|
| 260 |
+
nav_digital = gr.Button("✨ AI Digitizer (Create)", elem_classes=["nav-btn", "selected"])
|
| 261 |
+
nav_feed = gr.Button("📰 News Feed", elem_classes=["nav-btn"])
|
| 262 |
+
nav_saved = gr.Button("🔖 Saved Recipes", elem_classes=["nav-btn"])
|
| 263 |
+
nav_about = gr.Button("ℹ️ About Project", elem_classes=["nav-btn"])
|
| 264 |
|
| 265 |
+
gr.HTML("<hr style='border:0; border-top:1px solid #ced0d4; margin: 10px 0;'>")
|
| 266 |
+
gr.Markdown("### Your Shortcuts")
|
| 267 |
+
gr.Markdown("🥘 Culinary Arts Group\n\n🥧 Grandmother's Secrets\n\n🥗 Healthy Eating")
|
|
|
|
| 268 |
|
| 269 |
+
# === CENTER COLUMN (The Feed & App Logic) ===
|
| 270 |
+
with gr.Column(elem_classes=["col-feed"]):
|
| 271 |
|
| 272 |
+
# --- VIEW 1: AI DIGITIZER (Styled as "Create Post") ---
|
| 273 |
with gr.Group(visible=True) as digitalizer_view:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
|
| 275 |
+
# "Create Post" Card
|
| 276 |
+
with gr.Group(elem_classes=["fb-card"]):
|
| 277 |
+
gr.Markdown("### Create Post")
|
| 278 |
+
gr.HTML("<hr style='border:0; border-top:1px solid #e4e6eb; margin: 10px 0;'>")
|
|
|
|
|
|
|
| 279 |
|
| 280 |
+
with gr.Row():
|
| 281 |
+
input_img = gr.Image(type="filepath", label="Add to your post", height=250, container=True)
|
| 282 |
+
|
| 283 |
+
gr.HTML("<div style='margin-top:10px; font-size:14px; color:#65676B;'>Add to your post: 🟢 Photo/Video 👤 Tag People 📍 Location</div>")
|
| 284 |
+
magic_btn = gr.Button("Post", variant="primary") # Blue button
|
| 285 |
+
|
| 286 |
+
# Examples styled as small chips/thumbnails
|
| 287 |
+
gr.Examples(
|
| 288 |
+
examples=[["quick_tries_images/applecrisp.jpg"], ["quick_tries_images/meatballs recipe.jpg"]],
|
| 289 |
+
inputs=input_img, label="Quick Try (Click to load image)"
|
| 290 |
+
)
|
| 291 |
+
|
| 292 |
+
# Output ("The Resulting Post")
|
| 293 |
+
with gr.Group(elem_classes=["fb-card"]):
|
| 294 |
+
gr.HTML("""
|
| 295 |
+
<div class="post-header">
|
| 296 |
+
<div class="user-avatar" style="background:#1877F2;"></div>
|
| 297 |
+
<div class="post-info">
|
| 298 |
+
<div class="post-author">Legacy Kitchen AI</div>
|
| 299 |
+
<div class="post-meta">Just now · 🌍</div>
|
| 300 |
+
</div>
|
| 301 |
+
</div>
|
| 302 |
+
""")
|
| 303 |
+
out_text = gr.Textbox(label="Transcription Result", placeholder="Your digitized recipe text will appear here...", lines=10, show_label=False)
|
| 304 |
|
| 305 |
+
with gr.Row(elem_classes=["post-actions"]):
|
| 306 |
+
gr.Button("👍 Like", elem_classes=["action-btn"], size="sm")
|
| 307 |
+
gr.Button("💬 Comment", elem_classes=["action-btn"], size="sm")
|
| 308 |
+
gr.Button("↪ Share", elem_classes=["action-btn"], size="sm")
|
| 309 |
+
|
| 310 |
+
# Similar Recipes (Styled as "Suggested Posts")
|
| 311 |
+
gr.Markdown("### Suggested for you")
|
| 312 |
+
|
| 313 |
+
# Sim Result 1
|
| 314 |
+
with gr.Group(elem_classes=["fb-card"], visible=False) as c1_box:
|
| 315 |
+
sim1 = gr.Markdown("Similar Recipe 1")
|
| 316 |
+
with gr.Row(elem_classes=["post-actions"], visible=False) as c1_btns:
|
| 317 |
+
gr.Button("Save", elem_classes=["action-btn"], size="sm")
|
| 318 |
+
|
| 319 |
+
# Sim Result 2
|
| 320 |
+
with gr.Group(elem_classes=["fb-card"], visible=False) as c2_box:
|
| 321 |
+
sim2 = gr.Markdown("Similar Recipe 2")
|
| 322 |
+
with gr.Row(elem_classes=["post-actions"]):
|
| 323 |
+
gr.Button("Save", elem_classes=["action-btn"], size="sm")
|
| 324 |
+
|
| 325 |
+
# Sim Result 3
|
| 326 |
+
with gr.Group(elem_classes=["fb-card"], visible=False) as c3_box:
|
| 327 |
+
sim3 = gr.Markdown("Similar Recipe 3")
|
| 328 |
+
with gr.Row(elem_classes=["post-actions"]):
|
| 329 |
+
gr.Button("Save", elem_classes=["action-btn"], size="sm")
|
| 330 |
|
| 331 |
magic_btn.click(ui_update_pipeline, input_img, [out_text, sim1, c1_btns, c2_box, sim2, c3_box, sim3])
|
| 332 |
|
| 333 |
+
# --- VIEW 2: FEED ---
|
| 334 |
+
with gr.Column(visible=False) as feed_view:
|
| 335 |
+
if notdf_recipes.empty:
|
| 336 |
feed_samples = df_recipes.sample(10)
|
| 337 |
for index, row in feed_samples.iterrows():
|
| 338 |
user_name = random.choice(["Grandma Rose", "Chef Mike", "Sarah J."])
|
| 339 |
emoji = random.choice(["🥘", "🥗", "🍰", "🌮"])
|
| 340 |
+
post_time = random.choice(["2h", "3h", "6h", "Yesterday"])
|
| 341 |
|
| 342 |
+
with gr.Group(elem_classes=["fb-card"]):
|
| 343 |
+
# Custom HTML Header for the card
|
| 344 |
gr.HTML(f"""
|
| 345 |
+
<div class="post-header">
|
| 346 |
+
<div class="user-avatar" style="background:#e4e6eb; display:flex; align-items:center; justify-content:center; font-size:20px;">{emoji}</div>
|
| 347 |
+
<div class="post-info">
|
| 348 |
+
<div class="post-author">{user_name}</div>
|
| 349 |
+
<div class="post-meta">{post_time} · 🌍</div>
|
| 350 |
+
</div>
|
| 351 |
</div>
|
| 352 |
""")
|
| 353 |
+
gr.Markdown(f"**{row['Title']}**")
|
| 354 |
+
gr.Markdown(f"{str(row['Raw_Output'])[:300]}... <span style='color:#1877F2; cursor:pointer;'>See more</span>")
|
| 355 |
+
|
| 356 |
+
# Action Bar
|
| 357 |
+
with gr.Row(elem_classes=["post-actions"]):
|
| 358 |
+
gr.Button("👍 Like", elem_classes=["action-btn"], size="sm")
|
| 359 |
+
gr.Button("💬 Comment", elem_classes=["action-btn"], size="sm")
|
| 360 |
+
gr.Button("↪ Share", elem_classes=["action-btn"], size="sm")
|
| 361 |
else:
|
| 362 |
gr.Markdown("⚠️ Database is empty.")
|
| 363 |
|
| 364 |
+
# --- VIEW 3: SAVED RECIPES ---
|
| 365 |
with gr.Column(visible=False) as saved_view:
|
| 366 |
+
gr.Markdown("### 🔖 Saved Items")
|
|
|
|
| 367 |
if not df_recipes.empty:
|
| 368 |
+
saved_batch = df_recipes.head(20)
|
| 369 |
+
for index, row in saved_batch.iterrows():
|
| 370 |
+
with gr.Group(elem_classes=["fb-card"]):
|
| 371 |
+
with gr.Row():
|
| 372 |
+
gr.HTML("<div style='width:80px; height:80px; background:#eee; border-radius:8px;'></div>")
|
| 373 |
+
with gr.Column():
|
| 374 |
+
gr.Markdown(f"**{row['Title']}**")
|
| 375 |
+
gr.HTML("<span style='color:#65676B; font-size:12px;'>Saved from News Feed</span>")
|
| 376 |
+
gr.Button("View Collection", size="sm", variant="secondary")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
|
| 378 |
+
# --- VIEW 4: ABOUT ---
|
| 379 |
with gr.Group(visible=False) as about_view:
|
| 380 |
+
with gr.Group(elem_classes=["fb-card"]):
|
| 381 |
+
gr.Markdown("# About Legacy Kitchen")
|
| 382 |
+
gr.Markdown("Developed by **Shahar Firshtman** and **Lior Feinstein**.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
gr.HTML(f"""
|
| 384 |
<div style="margin-top: 20px;">
|
| 385 |
+
<img src="data:image/jpeg;base64,{process_b64}" style="width: 100%; border-radius: 8px;">
|
|
|
|
| 386 |
</div>
|
| 387 |
""")
|
| 388 |
|
| 389 |
+
# === RIGHT COLUMN (Contacts / Sponsored) ===
|
| 390 |
+
with gr.Column(elem_classes=["col-right"]):
|
| 391 |
+
gr.Markdown("### Sponsored")
|
| 392 |
+
with gr.Group(elem_classes=["fb-card"]):
|
| 393 |
+
gr.Markdown("**Culinary School**\nJoin 10,000+ students learning to cook today!")
|
| 394 |
+
gr.Image("https://picsum.photos/300/150", show_label=False, interactive=False, height=150)
|
| 395 |
+
|
| 396 |
+
gr.HTML("<hr style='border:0; border-top:1px solid #ced0d4; margin: 10px 0;'>")
|
| 397 |
|
| 398 |
+
gr.Markdown("### Contacts")
|
| 399 |
+
# Fake contacts list
|
| 400 |
+
contacts = ["Elon Musk", "Gordon Ramsay", "Jamie Oliver", "Martha Stewart"]
|
| 401 |
+
for contact in contacts:
|
| 402 |
+
gr.HTML(f"""
|
| 403 |
+
<div class="contact-row">
|
| 404 |
+
<div style="position:relative;">
|
| 405 |
+
<img src="https://ui-avatars.com/api/?name={contact}&background=random" style="width:36px; height:36px; border-radius:50%;">
|
| 406 |
+
<div style="position:absolute; bottom:0; right:0; width:10px; height:10px; background:#31a24c; border-radius:50%; border:2px solid white;"></div>
|
| 407 |
+
</div>
|
| 408 |
+
<div class="contact-name">{contact}</div>
|
| 409 |
+
</div>
|
| 410 |
+
""")
|
| 411 |
|
| 412 |
# ==========================================
|
| 413 |
+
# 6. JAVASCRIPT NAVIGATION LOGIC
|
| 414 |
# ==========================================
|
| 415 |
def go_digi():
|
| 416 |
return (
|
|
|
|
| 439 |
nav_feed.click(go_feed, None, outputs_ui)
|
| 440 |
nav_saved.click(go_saved, None, outputs_ui)
|
| 441 |
nav_about.click(go_about, None, outputs_ui)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 442 |
|
| 443 |
if __name__ == "__main__":
|
| 444 |
+
demo.launch()
|