K2-k2/VibeCraft-AI
0
1import streamlit as st2import google.generativeai as genai3from PIL import Image4import streamlit.components.v1 as components5import os6import base647 8# --- 1. LOGO PREPARATION ---9logo_path = "logo.png"10 11def get_base64_image(path):12 try:13 with open(path, "rb") as f:14 return base64.b64encode(f.read()).decode()15 except:16 return ""17 18logo_base64 = get_base64_image(logo_path)19 20# --- 2. PAGE CONFIG & STYLING ---21st.set_page_config(page_title="VibeCraft AI", layout="wide", page_icon=logo_path)22 23# Enhanced CSS for side-by-side alignment on all devices24st.markdown(f"""25<style>26 /* Force Sidebar & Main Header to be Flex (Side-by-Side) on Mobile/Desktop */27 .brand-container {{28 display: flex;29 align-items: center;30 gap: 12px;31 margin-bottom: 10px;32 }}33 .brand-text {{34 font-weight: 800;35 margin: 0;36 line-height: 1.1;37 font-family: 'Helvetica Neue', Arial, sans-serif;38 }}39 .main-title {{ font-size: clamp(1.4rem, 5vw, 2.2rem); color: #1c1e21; }}40 .sidebar-title {{ font-size: 1.1rem; color: #1c1e21; }}41 42 .caption-draft-card {{43 background-color: #f9f9f9;44 padding: 20px;45 border-radius: 12px;46 border: 1px solid #e1e8ed;47 margin-bottom: 5px;48 }}49 .caption-text {{50 font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;51 font-size: 16px;52 color: #1c1e21;53 line-height: 1.6;54 white-space: pre-wrap;55 }}56 .feel-header-clean {{57 color: #1c1e21;58 font-weight: bold;59 font-size: 17px;60 margin-bottom: 5px;61 text-transform: uppercase;62 display: inline-block;63 }}64 /* Fix for invisible gray box */65 iframe {{ background: transparent !important; border: none !important; }}66</style>67""", unsafe_allow_html=True)68 69# --- 3. THE IMPROVED COPY BUTTON (VISIBLE & WORKING) ---70def copy_button_js(text, label, key):71 # Fixed height=75 ensures the button is not cut off on mobile72 js_code = f"""73 <div id="container_{key}">74 <script>75 function copyText_{key}() {{76 const textToCopy = `{text.replace("`", "\\`").replace("$", "\\$")}`;77 navigator.clipboard.writeText(textToCopy).then(() => {{78 const btn = document.getElementById('btn_{key}');79 btn.innerHTML = "โ
Copied!";80 btn.style.backgroundColor = "#000000";81 btn.style.color = "#ffffff";82 setTimeout(() => {{ 83 btn.innerHTML = "๐ {label}"; 84 btn.style.backgroundColor = "#ffffff";85 btn.style.color = "#000000";86 }}, 2000);87 }});88 }}89 </script>90 <button id="btn_{key}" onclick="copyText_{key}()" style="91 background-color: #ffffff; border: 1px solid #000000;92 color: #000000; padding: 10px 20px; border-radius: 8px;93 cursor: pointer; font-size: 14px; font-weight: 600;94 display: flex; align-items: center; width: 100%;95 justify-content: center; transition: 0.3s;96 ">97 ๐ {label}98 </button>99 </div>100 """101 components.html(js_code, height=75)102 103# --- 4. SIDEBAR BRANDING (ALWAYS SIDE-BY-SIDE) ---104with st.sidebar:105 st.markdown(f"""106 <div class="brand-container">107 <img src="data:image/png;base64,{logo_base64}" width="32">108 <h2 class="brand-text sidebar-title">VibeCraft AI</h2>109 </div>110 """, unsafe_allow_html=True)111 112 st.markdown("---")113 st.title("โ๏ธ Settings")114 api_key = st.text_input("Gemini API Key (optional)", type="password")115 selected_vibe = st.selectbox("Choose the Vibe:", ["Gen-Z / Witty", "Desi / Trendy", "Shayari / Poetic", "Hindi / Desi", "Hinglish / Urban", "Professional", "Aesthetic / Poetic", "Minimalist", "Savage", "Educational", "Storyteller", "Hype / Energetic"])116 117# --- 5. MAIN HEADER (ALWAYS SIDE-BY-SIDE) ---118st.markdown(f"""119 <div class="brand-container">120 <img src="data:image/png;base64,{logo_base64}" width="55">121 <div>122 <h1 class="brand-text main-title">VibeCraft AI</h1>123 <p style="margin:0; color:#65676b; font-size:0.9rem;">โจ <i>Turn your raw visuals into viral vibes</i></p>124 </div>125 </div>126""", unsafe_allow_html=True)127 128st.divider()129 130# --- 6. CORE APP LOGIC ---131uploaded_file = st.file_uploader("Upload Image...", type=["jpg", "jpeg", "png"])132 133if uploaded_file:134 img = Image.open(uploaded_file)135 st.subheader("Selected Image:")136 st.image(img, width=280) 137 st.divider()138 139 if st.button("โจ Generate Captions", key="write_btn"):140 # API Key Logic141 if api_key:142 final_key = api_key143 else:144 try:145 # Fetch from Hugging Face Secrets146 final_key = os.getenv("GOOGLE_API_KEY_N")147 except:148 st.error("Please enter an API Key in sidebar or configure hugging face Secrets!")149 st.stop()150 151 genai.configure(api_key=final_key)152 model = genai.GenerativeModel('gemini-2.5-flash-lite')153 154 with st.spinner("AI is crafting vibes..."):155 try:156 # Determine the language and script instruction157 if "Hinglish" in selected_vibe:158 lang_instr = "Write in Hinglish (Hindi words using English/Latin alphabet)."159 elif any(word in selected_vibe for word in ["Desi", "Shayari", "Hindi"]):160 lang_instr = "Write strictly in Hindi using Devanagari script."161 else:162 lang_instr = "Write the captions in English."163 164 prompt = f"Analyze this image. Vibe: {selected_vibe}.{lang_instr}. Write 3 creative captions. Format: ### FEEL: [Header] TEXT: [Caption + Hashtags]"165 response = model.generate_content([prompt, img])166 if response.text:167 raw_parts = response.text.split('###')168 processed = []169 for part in raw_parts:170 if "FEEL:" in part and "TEXT:" in part:171 f = part.split("FEEL:")[1].split("TEXT:")[0].strip()172 t = part.split("TEXT:")[1].strip()173 processed.append({"feel": f, "text": t})174 st.session_state.results = processed175 except Exception as e:176 st.info("You have reached the limit. Please wait a minute or try again tomorrow!")177 st.error(f"{e}")178 179else:180 st.info("Please upload an image to get viral captions")181 182# --- 7. DISPLAY RESULTS ---183if st.session_state.get('results'):184 st.subheader(f"Drafts for: {selected_vibe}")185 for i, item in enumerate(st.session_state.results[:3]):186 st.markdown(f"""187 <div class="caption-draft-card">188 <div class="feel-header-clean">โจ {item['feel']}</div>189 <div class="caption-text">{item['text']}</div>190 </div>191 """, unsafe_allow_html=True)192 copy_button_js(item['text'], f"Copy Caption {i+1}", f"btn_{i}")193 st.write("") 194 195 if st.button("Clear All"):196 st.session_state.results = None197 st.rerun()198 199 