Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import random | |
| from io import BytesIO | |
| from pathlib import Path | |
| from PIL import Image, ImageDraw, ImageFont | |
| # ------------------- BRAND CONFIG ------------------- | |
| BRAND = "#0F2C59" # Procelevate navy | |
| ACCENT = "#00FFFF" # Cyan glow | |
| PINK = "#FF00FF" # Magenta glow | |
| st.set_page_config( | |
| page_title="AI Name Magic - Procelevate", | |
| page_icon="โจ", | |
| layout="centered", | |
| ) | |
| # ------------------- SESSION STATE ------------------- | |
| if "gallery" not in st.session_state: | |
| st.session_state.gallery = [] # list of dicts: {"img": bytes, "name": str, "fact": str} | |
| if "current_name" not in st.session_state: | |
| st.session_state.current_name = "" | |
| if "current_fact" not in st.session_state: | |
| st.session_state.current_fact = "" | |
| if "current_card" not in st.session_state: | |
| st.session_state.current_card = None | |
| # ------------------- HEADER (CENTERED LOGO) ------------------- | |
| # Try a few common locations for the logo so path changes don't break the app. | |
| logo_candidates = [ | |
| "src/procelevate_logo.png", | |
| "src/assets/procelevate_logo.png", | |
| "procelevate_logo.png", | |
| ] | |
| logo_path = next((p for p in logo_candidates if Path(p).exists()), None) | |
| if logo_path: | |
| c1, c2, c3 = st.columns([1, 2, 1]) | |
| with c2: | |
| st.image(logo_path, width=150) | |
| st.markdown( | |
| f""" | |
| <div style="text-align:center; margin-top:4px;"> | |
| <h1 style="margin:0; color:{BRAND};">AI Name Magic โจ</h1> | |
| <div style="font-size:20px; color:#1f2937;">Powered by <b style="color:{BRAND};">Procelevate Consulting</b></div> | |
| </div> | |
| <hr style="opacity:.2;"/> | |
| """, | |
| unsafe_allow_html=True, | |
| ) | |
| # ------------------- INPUT FORM ------------------- | |
| with st.form("name_form", clear_on_submit=False): | |
| st.subheader("๐ฎ Type Your Name & See AI Magic") | |
| name = st.text_input("Enter your name:") | |
| submitted = st.form_submit_button("Show My AI Prediction ๐") | |
| # ------------------- PREDICTION POOL ------------------- | |
| facts = [ | |
| "It's a beautiful name...AI predicts youโll be a Future Innovator. Keep rocking..! ๐", | |
| "It's a beautiful name...Your curiosity will power the next big idea. Keep inspiring...! ๐ก", | |
| "It's a beautiful name...You might be the Next Data Scientist / Data Engineer. Best Wishes for you....! ๐", | |
| "It's a beautiful name...AI says youโll shape the Future of Tech. Thank you future CTO...! ๐ค", | |
| "It's a beautiful name...Leadership and technology is in your DNA ๐", | |
| "It's a beautiful name...AI foresees a potential CEO / CTO of a big company ๐", | |
| ] | |
| def choose_new_fact(exclude=None): | |
| pool = [f for f in facts if f != exclude] or facts | |
| return random.choice(pool) | |
| # ------------------- IMAGE GENERATOR (PNG CARD) ------------------- | |
| def _load_font(path_candidates, size): | |
| """Try to load a TTF font from common paths; fall back to default.""" | |
| for p in path_candidates: | |
| if Path(p).exists(): | |
| try: | |
| return ImageFont.truetype(p, size) | |
| except Exception: | |
| pass | |
| return ImageFont.load_default() | |
| def make_card(name_text: str, fact_text: str) -> bytes: | |
| """Create a branded 'Future Me Card' PNG and return bytes.""" | |
| W, H = 1200, 630 | |
| base = Image.new("RGB", (W, H), (15, 44, 89)) # #0F2C59 | |
| draw = ImageDraw.Draw(base) | |
| # Subtle cyan gradient overlay | |
| grad = Image.new("RGB", (W, H)) | |
| for y in range(H): | |
| c = int(255 * (y / H) * 0.18) | |
| grad.putpixel((0, y), (0, 180, 180 - c)) | |
| grad = grad.resize((W, H)) | |
| base = Image.blend(base, grad, 0.15) | |
| draw = ImageDraw.Draw(base) | |
| # Fonts: try DejaVu (available if installed), else default | |
| dejavu_bold = [ | |
| "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", | |
| "/usr/local/share/fonts/DejaVuSans-Bold.ttf", | |
| ] | |
| dejavu = [ | |
| "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", | |
| "/usr/local/share/fonts/DejaVuSans.ttf", | |
| ] | |
| title_font = _load_font(dejavu_bold, 70) | |
| name_font = _load_font(dejavu_bold, 120) | |
| fact_font = _load_font(dejavu, 46) | |
| foot_font = _load_font(dejavu, 28) | |
| # Paste logo if available (resolve relative to this file) | |
| try: | |
| local_dir = Path(__file__).parent | |
| lp = None | |
| for cand in ["procelevate_logo.png", "assets/procelevate_logo.png", "../procelevate_logo.png"]: | |
| p = (local_dir / cand).resolve() | |
| if p.exists(): | |
| lp = p | |
| break | |
| if lp: | |
| logo = Image.open(lp).convert("RGBA") | |
| logo = logo.resize((140, 140)) | |
| base.paste(logo, (50, 50), logo) | |
| except Exception: | |
| pass # OK if logo missing | |
| # Title pill | |
| title = "YOUR AI PREDICTION" | |
| tw = draw.textlength(title, font=title_font) | |
| th = getattr(title_font, "size", 70) | |
| draw.rounded_rectangle([(400, 70), (400 + tw + 60, 70 + th + 40)], radius=18, fill=(255, 255, 255)) | |
| draw.text((430, 90), title, font=title_font, fill=(15, 44, 89)) | |
| # Neon name | |
| name_text = name_text.upper() | |
| nx, ny = 120, 260 | |
| draw.text((nx + 6, ny + 6), name_text, font=name_font, fill=(255, 0, 255)) # magenta shadow | |
| draw.text((nx - 6, ny - 6), name_text, font=name_font, fill=(0, 255, 255)) # cyan shadow | |
| draw.text((nx, ny), name_text, font=name_font, fill=(255, 255, 255)) # white main | |
| # Fact chip | |
| fact_box_y = ny + 170 | |
| draw.rounded_rectangle([(80, fact_box_y), (W - 80, fact_box_y + 170)], radius=24, fill=(255, 255, 255)) | |
| draw.text((110, fact_box_y + 55), f"โจ {fact_text}", font=fact_font, fill=(15, 44, 89)) | |
| # Footer | |
| footer = "ยฉ Procelevate Consulting โข Elevating Processes. Empowering People." | |
| fw = draw.textlength(footer, font=foot_font) | |
| draw.text(((W - fw) // 2, H - 60), footer, font=foot_font, fill=(230, 240, 245)) | |
| buf = BytesIO() | |
| base.save(buf, format="PNG") | |
| buf.seek(0) | |
| return buf.getvalue() | |
| # ------------------- RENDER RESULT BLOCK ------------------- | |
| def render_result(name_text: str, fact_text: str): | |
| st.markdown( | |
| f""" | |
| <div style=" | |
| margin: 10px auto 0 auto; | |
| max-width: 820px; | |
| text-align:center; | |
| padding: 18px 22px; | |
| border-radius: 16px; | |
| background: | |
| radial-gradient(circle at 20% 10%, rgba(0,255,255,.25), transparent 40%), | |
| radial-gradient(circle at 80% 0%, rgba(255,0,255,.25), transparent 35%), | |
| linear-gradient(180deg, #ffffff 0%, #f7fbff 100%); | |
| border: 2px solid #e6eef7;"> | |
| <div style="font-size:14px; letter-spacing:1px; color:{BRAND}; font-weight:700; | |
| display:inline-block; padding:6px 12px; border-radius:999px; background:#e6f3ff; margin-bottom:8px;"> | |
| YOUR AI PREDICTION | |
| </div> | |
| <h1 style="margin:4px 0 8px 0; font-size:56px; color:{BRAND}; | |
| text-shadow: 2px 2px {ACCENT}, -2px -2px {PINK};"> | |
| {name_text.upper()} | |
| </h1> | |
| <div style="font-size:22px; color:#0b2239; padding:12px 16px; border-radius:12px; | |
| background:#f0f9ff; display:inline-block;"> | |
| โจ {fact_text} | |
| </div> | |
| </div> | |
| """, | |
| unsafe_allow_html=True, | |
| ) | |
| # ------------------- HANDLE SUBMIT ------------------- | |
| if submitted and name.strip(): | |
| st.session_state.current_name = name.strip() | |
| st.session_state.current_fact = choose_new_fact(None) | |
| st.session_state.current_card = make_card(st.session_state.current_name, st.session_state.current_fact) | |
| # Add to gallery (cap 5 latest) | |
| st.session_state.gallery = (st.session_state.gallery + [{ | |
| "img": st.session_state.current_card, | |
| "name": st.session_state.current_name, | |
| "fact": st.session_state.current_fact | |
| }])[-5:] | |
| st.balloons() | |
| elif submitted and not name.strip(): | |
| st.warning("Please enter your name first ๐") | |
| # ------------------- TRY ANOTHER PREDICTION BUTTON ------------------- | |
| # Show only if we already have a name | |
| if st.session_state.current_name: | |
| try_another = st.button("๐ Try another prediction") | |
| if try_another: | |
| st.session_state.current_fact = choose_new_fact(st.session_state.current_fact) | |
| st.session_state.current_card = make_card(st.session_state.current_name, st.session_state.current_fact) | |
| st.session_state.gallery = (st.session_state.gallery + [{ | |
| "img": st.session_state.current_card, | |
| "name": st.session_state.current_name, | |
| "fact": st.session_state.current_fact | |
| }])[-5:] | |
| # ------------------- SHOW RESULT (IF ANY) ------------------- | |
| if st.session_state.current_name and st.session_state.current_fact: | |
| render_result(st.session_state.current_name, st.session_state.current_fact) | |
| # Download button for the latest card | |
| if st.session_state.current_card: | |
| st.download_button( | |
| "โฌ๏ธ Download My Future Me Card (PNG)", | |
| data=st.session_state.current_card, | |
| file_name=f"{st.session_state.current_name.replace(' ','_')}_future_me.png", | |
| mime="image/png", | |
| ) | |
| # ------------------- LIVE GALLERY (LAST 5) ------------------- | |
| if st.session_state.gallery: | |
| st.markdown("<hr style='opacity:.15;'/>", unsafe_allow_html=True) | |
| st.subheader("๐ธ Live Gallery (latest 5)") | |
| items = list(reversed(st.session_state.gallery)) # newest first | |
| cols = st.columns(len(items)) | |
| for col, item in zip(cols, items): | |
| with col: | |
| st.image(item["img"], caption=item["name"], use_container_width=True) | |
| st.markdown("<hr style='opacity:.2;'/>", unsafe_allow_html=True) | |
| st.markdown( | |
| "<p style='text-align:center; font-size:14px;'>ยฉ 2025 Procelevate Consulting</p>", | |
| unsafe_allow_html=True, | |
| ) | |