"""Module 6 - Voice Notes: browser dictation + Saathi response. Streamlit 1.32 does not include a native microphone-to-text widget. To stay dependency-light for HF Spaces, this module uses the browser Web Speech API inside a local HTML component. The transcript stays in the browser until the user pastes it into the Streamlit text area. """ from __future__ import annotations import json from typing import Dict, List import streamlit as st import streamlit.components.v1 as components from backend.claude_client import chat from backend.i18n import claude_language_name, t from backend.safeguards import check_crisis, render_crisis_banner MODULE_NAME = "voice_notes" NOTE_KEY = "voice_note" RESPONSE_KEY = "voice_response" HISTORY_KEY = "voice_history" VOICE_LANG_TAGS = { "en": "en-IN", "hi": "hi-IN", "bn": "bn-IN", "ta": "ta-IN", "te": "te-IN", "mr": "mr-IN", "ur": "ur-IN", } def _init_state() -> None: if NOTE_KEY not in st.session_state: st.session_state[NOTE_KEY] = "" if RESPONSE_KEY not in st.session_state: st.session_state[RESPONSE_KEY] = "" if HISTORY_KEY not in st.session_state: st.session_state[HISTORY_KEY] = [] def _append_history(role: str, content: str) -> None: history = list(st.session_state.get(HISTORY_KEY, [])) history.append({"role": role, "content": content}) st.session_state[HISTORY_KEY] = history def _render_voice_component(lang: str) -> None: cfg = { "lang": VOICE_LANG_TAGS.get(lang, "en-IN"), "start": t("voice_start_button", lang), "stop": t("voice_stop_button", lang), "copy": t("voice_copy_button", lang), "clear": t("voice_clear_button", lang), "transcript": t("voice_transcript_label", lang), "unsupported": t("voice_not_supported", lang), } cfg_json = json.dumps(cfg, ensure_ascii=False) components.html( f"""