import gradio as gr from carebridge_client import CareBridgeTranslator # --- Initialize Client (Lazy) --- translator = None def load_translator(): global translator if translator is None: translator = CareBridgeTranslator() return translator # --- Languages --- LANGUAGES = [ "English", "Polish", "Romanian", "Punjabi", "Urdu", "Portuguese", "Spanish", "Arabic", "Bengali", "Gujarati", "Italian" ] # --- Minimal CSS (Gemini Dictation Inspired) --- CUSTOM_CSS = """ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); * { font-family: 'Inter', sans-serif !important; } .gradio-container { max-width: 900px !important; margin: auto !important; background: #f8fafc !important; min-height: 100vh !important; } /* --- Header --- */ .app-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 24px; border-bottom: 1px solid #e2e8f0; background: white; } .app-title { font-size: 1.25rem; font-weight: 700; color: #1e293b; } .lang-pills { display: flex; gap: 8px; align-items: center; } .lang-pill { background: #e0e7ff; color: #4338ca; padding: 6px 14px; border-radius: 20px; font-size: 0.85rem; font-weight: 500; } .lang-arrow { color: #94a3b8; font-size: 1.2rem; } /* --- Document Area --- */ .document-area { background: white; border-radius: 16px; margin: 24px; padding: 32px; min-height: 400px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); border: 1px solid #e2e8f0; } .output-text { font-size: 1.75rem !important; line-height: 1.6 !important; color: #1e293b !important; min-height: 200px !important; text-align: center; padding: 40px 20px; } .placeholder-text { color: #94a3b8; font-style: italic; } /* --- Floating Controls --- */ .floating-bar { position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); display: flex; gap: 16px; align-items: center; background: white; padding: 12px 24px; border-radius: 40px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); z-index: 100; } .fab-btn { width: 56px !important; height: 56px !important; border-radius: 50% !important; font-size: 1.5rem !important; display: flex !important; align-items: center !important; justify-content: center !important; border: none !important; cursor: pointer !important; transition: transform 0.2s, box-shadow 0.2s !important; } .fab-btn:hover { transform: scale(1.1) !important; box-shadow: 0 4px 12px rgba(0,0,0,0.2) !important; } .fab-primary { background: linear-gradient(135deg, #4F46E5, #6366f1) !important; color: white !important; width: 72px !important; height: 72px !important; } .fab-secondary { background: #f1f5f9 !important; color: #475569 !important; } /* Hide Gradio footer */ footer { display: none !important; } /* Input modals */ .input-modal { background: white; border-radius: 16px; padding: 24px; margin: 0 24px 100px 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); border: 1px solid #e2e8f0; } """ # --- Translation Functions --- def translate_text(text, source_lang, target_lang): if not text: return "Enter text above to translate..." yield "⏳ Translating..." t = load_translator() result = t.translate_text(text, source_lang, target_lang) yield result def translate_speech(audio, source_lang, target_lang): if audio is None: return "Record audio using the microphone..." yield "🎧 Processing speech..." t = load_translator() result = t.translate_audio(audio, source_lang, target_lang) yield result def translate_document(image, source_lang, target_lang): if image is None: return "Upload a document image..." yield "📄 Scanning document..." t = load_translator() result = t.translate_image(image, source_lang, target_lang) yield result def get_tts(text, lang): if not text or text.startswith("⏳") or text.startswith("🎧") or text.startswith("📄"): return None t = load_translator() return t.speak_text(text, lang) # --- App Layout --- with gr.Blocks(css=CUSTOM_CSS, title="SIMBOTI Live") as app: # State for current mode mode = gr.State("text") # --- Header --- with gr.Row(elem_classes="app-header"): gr.HTML("🌐 SIMBOTI") with gr.Row(elem_classes="lang-pills"): source_lang = gr.Dropdown(LANGUAGES, value="English", show_label=False, container=False, scale=0, min_width=120) gr.HTML("") target_lang = gr.Dropdown(LANGUAGES, value="Polish", show_label=False, container=False, scale=0, min_width=120) # --- Document Output Area --- with gr.Column(elem_classes="document-area"): output_display = gr.Textbox( value="Your translation will appear here...", show_label=False, interactive=False, lines=8, elem_classes="output-text" ) audio_output = gr.Audio(label="Listen", autoplay=True, visible=True) # --- Input Section (Toggleable) --- with gr.Column(elem_classes="input-modal", visible=True) as text_input_section: text_input = gr.Textbox(label="Type your message", placeholder="e.g., Where does it hurt?", lines=2) text_submit = gr.Button("Translate", variant="primary") with gr.Column(elem_classes="input-modal", visible=False) as audio_input_section: audio_input = gr.Audio(sources=["microphone"], type="filepath", label="🎤 Record") audio_submit = gr.Button("Translate Speech", variant="primary") with gr.Column(elem_classes="input-modal", visible=False) as doc_input_section: doc_input = gr.Image(type="pil", label="📷 Upload Document") doc_submit = gr.Button("Scan & Translate", variant="primary") # --- Mode Switchers --- with gr.Row(elem_classes="floating-bar"): text_mode_btn = gr.Button("⌨️", elem_classes="fab-btn fab-secondary") mic_mode_btn = gr.Button("🎤", elem_classes="fab-btn fab-primary") doc_mode_btn = gr.Button("📎", elem_classes="fab-btn fab-secondary") # --- Mode Toggle Logic --- def show_text_mode(): return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False) def show_audio_mode(): return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False) def show_doc_mode(): return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True) text_mode_btn.click(show_text_mode, outputs=[text_input_section, audio_input_section, doc_input_section]) mic_mode_btn.click(show_audio_mode, outputs=[text_input_section, audio_input_section, doc_input_section]) doc_mode_btn.click(show_doc_mode, outputs=[text_input_section, audio_input_section, doc_input_section]) # --- Translation Triggers --- text_submit.click(translate_text, inputs=[text_input, source_lang, target_lang], outputs=[output_display]).then( get_tts, inputs=[output_display, target_lang], outputs=[audio_output] ) audio_submit.click(translate_speech, inputs=[audio_input, source_lang, target_lang], outputs=[output_display]).then( get_tts, inputs=[output_display, target_lang], outputs=[audio_output] ) doc_submit.click(translate_document, inputs=[doc_input, source_lang, target_lang], outputs=[output_display]).then( get_tts, inputs=[output_display, target_lang], outputs=[audio_output] ) # Launch if __name__ == "__main__": app.launch(ssr_mode=False)