""" ui/components.py ────────────────────────────────────────────────────────────────────────────── VoiceVerse Pro — Shared Presentational Components All raw HTML/CSS and reusable Streamlit snippets live here. No business logic. No session state mutations. Every function is a pure renderer. """ from __future__ import annotations import streamlit as st # ────────────────────────────────────────────────────────────────────────────── # Global stylesheet (injected once by app.py) # ────────────────────────────────────────────────────────────────────────────── def inject_css() -> None: """Inject the global dark-studio stylesheet into the Streamlit page.""" st.markdown(""" """, unsafe_allow_html=True) # ────────────────────────────────────────────────────────────────────────────── # Header # ────────────────────────────────────────────────────────────────────────────── def render_header() -> None: st.markdown("""

🎙️ VoiceVerse Pro

Modular RAG · Mistral-24B · Synthetic Audio Studio

2026 STABLE BUILD · ML ASSIGNMENT 2
""", unsafe_allow_html=True) # ────────────────────────────────────────────────────────────────────────────── # Stage tracker bar # ────────────────────────────────────────────────────────────────────────────── _STAGE_LABELS = [ "① Upload & Index", "② Retrieve Context", "③ Generate Script", "④ Synthesize Audio", ] def render_stage_tracker(current_stage: int) -> None: pills = "".join( f" i else ' pending'}'>" f"{'✓ ' if current_stage > i else ''}{label}" for i, label in enumerate(_STAGE_LABELS) ) st.markdown(pills, unsafe_allow_html=True) st.markdown("") # ────────────────────────────────────────────────────────────────────────────── # Mode selector — prominent pill toggle in main area # ────────────────────────────────────────────────────────────────────────────── def render_mode_selector() -> str: """ Render a prominent two-button mode toggle in the main content area. Persists selection in st.session_state["output_mode"]. Returns the selected mode value string (matches OutputMode.value). """ # Bootstrap default if "output_mode" not in st.session_state: st.session_state["output_mode"] = "Audio Transcript" current = st.session_state["output_mode"] st.markdown(""" """, unsafe_allow_html=True) col_t, col_p = st.columns(2) with col_t: transcript_active = current == "Audio Transcript" if st.button( "🎙️ Audio Transcript", use_container_width=True, type="primary" if transcript_active else "secondary", key="mode_btn_transcript", ): st.session_state["output_mode"] = "Audio Transcript" st.rerun() with col_p: podcast_active = current == "Podcast (2 Speakers)" if st.button( "🎭 Podcast — 2 Speakers", use_container_width=True, type="primary" if podcast_active else "secondary", key="mode_btn_podcast", ): st.session_state["output_mode"] = "Podcast (2 Speakers)" st.rerun() # Visual label showing what's active icon = "🎙️" if current == "Audio Transcript" else "🎭" st.markdown( f"

" f"{icon} Active: {current}

", unsafe_allow_html=True, ) return st.session_state["output_mode"] # ────────────────────────────────────────────────────────────────────────────── # Reusable card primitives # ────────────────────────────────────────────────────────────────────────────── def file_card(label: str, filename: str, detail: str) -> None: st.markdown( f"
" f"
{label}
" f"{filename}
" f"{detail}" f"
", unsafe_allow_html=True, ) def chunk_card(index: int, source: str, page, chunk_id, content: str) -> None: preview = content[:600] + ("…" if len(content) > 600 else "") st.markdown( f"
" f"
CHUNK {index} · {source} " f"· Page {page} · ID {chunk_id}
" f"{preview}" f"
", unsafe_allow_html=True, ) def script_box(text: str) -> None: st.markdown( f"
{text}
", unsafe_allow_html=True, )