"""Split screen for the projector (#17): patient on the left, doctor on the right, clock on top. Left: the patient web app in an iframe when `PATIENT_WEB_URL` is set (React app, e.g. http://localhost:5173), otherwise the console's fallback web chat. Right: compact doctor panel polled every 2 s with a red banner on a new acute alert. """ import sys from pathlib import Path import streamlit as st # `streamlit run dashboard/app.py` already puts dashboard/ on sys.path; this keeps the page runnable on its own. sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from console import widgets as w # noqa: E402 st.set_page_config(page_title="Сплит-экран", page_icon="🖥️", layout="wide", initial_sidebar_state="collapsed") st.markdown( "", unsafe_allow_html=True, ) persona = w.select_persona() w.show_flash() token = persona["token"] top, play = st.columns([6, 1.3], vertical_alignment="center") with top: w.render_clock_bar(persona, key="split-clock", with_reset=False) with play: scenario, _ = w.scenario_for(persona) runner = w.make_runner(persona, delay=0.8) nxt = runner.next_day(scenario) if scenario.days else None clicked = st.button( f"▶ Сценарий: день {nxt.day}" if nxt else "Сценарий проигран", disabled=nxt is None, width="stretch", help="Проиграть следующий день из data/synthetic/<персона>.jsonl", ) if clicked and nxt is not None: w.execute(runner.play_day(scenario, nxt.day), w.playback_state(token), title=f"День {nxt.day}") st.rerun() left, right = st.columns([1.1, 1], gap="large") with left: url = w.patient_web_url() if url: st.markdown(f"#### 📱 Пациент — {persona.get('display_name') or token}") st.iframe(url, height=760) st.caption(f"Веб-приложение пациента: {url}") else: st.markdown(f"#### 💬 Пациент — {persona.get('display_name') or token}") w.render_chat(token, height=600, key="split-chat") with right: st.markdown("#### 🩺 Кабинет врача — д-р Смирнова Е. А.") w.polled_doctor_panel(token, key="split-doc")