// vita-app.jsx — flow orchestration: splash → choose Vita → conversation → clinical history
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"persona": "calida",
"waveStyle": "barras",
"dark": true,
"tts": true,
}/*EDITMODE-END*/;
// ── Screen background wrapper ───────────────────────────────────
function Stage({ t, children, pad = true }) {
// Antes: position absolute sobre el lienzo fijo de 402x874 del marco de
// iPhone. Ahora fluye: el alto lo decide el contenido y el shell se encarga
// del scroll. `pad` se conserva por compatibilidad con las llamadas actuales.
void pad;
return (
{children}
);
}
// ── 1. Splash ───────────────────────────────────────────────────
function Splash({ t, persona, waveStyle, onStart }) {
const p = t.paleta;
// El contenido se agrupa en una tarjeta centrada con ancho acotado. Antes
// se estiraba a todo el ancho de la ventana y el botón quedaba pegado al
// texto: en escritorio parecía un móvil deformado, no una pantalla web.
return (
Conoce a
Vita
Tu médica de confianza, por voz. Te acompaña, recuerda tu historia y cuida tu longevidad — 24 horas, todos los días.
{Icon.shield({ s: 14, c: p.textMuted })} Tus datos son tuyos. VITA solo los custodia.
);
}
function BigButton({ t, onClick, children, secondary }) {
return (
);
}
// ── 2. Choose your Vita ─────────────────────────────────────────
// Frase de muestra por personalidad. Cada una está escrita en el registro
// que le corresponde, así el paciente oye la diferencia real de tono, no
// sólo la de los diales de voz.
const MUESTRA_PERSONA = {
calida: 'Hola, soy Vita. Me alegra mucho conocerte. Cuéntame, ¿cómo te has sentido estos días?',
serena: 'Hola, soy Vita. Estoy aquí contigo. Respira tranquilo, y cuando quieras me cuentas cómo te sientes.',
coach: '¡Hola! Soy Vita. Vamos a cuidar tu salud juntos. Cuéntame cómo te has sentido y arrancamos.',
profesional: 'Hola, soy Vita, tu asistente de salud. Voy a hacerte algunas preguntas para conocer tu estado de salud. ¿Cómo te has sentido?',
cercano: 'Hola, soy Vita. Estoy aquí para acompañarte en el cuidado de tu salud. Cuéntame, ¿cómo te has sentido estos días?',
clinica: 'Hola, soy Vita, tu asistente de salud. Empecemos con calma: cuéntame cómo te has sentido últimamente.',
};
// Las personas de la voz principal se ofrecen siempre; las demás, sólo si el
// servidor tiene su voz configurada (VitaCore las consulta a /api/tts).
const PERSONAS_BASE = ['calida', 'serena', 'coach'];
function usePersonasDisponibles() {
const [ids, setIds] = React.useState(PERSONAS_BASE);
React.useEffect(() => {
const nucleo = window.VitaCore;
if (!nucleo || !nucleo.voz.personasDisponibles) return;
let vigente = true;
nucleo.voz.personasDisponibles()
.then((lista) => { if (vigente && Array.isArray(lista) && lista.length) setIds(lista); })
.catch(() => {});
return () => { vigente = false; };
}, []);
return ids;
}
function ChooseVita({ t, current, waveStyle, onPick, onContinue, onBack, voice, tts }) {
const p = t.paleta;
const [sonando, setSonando] = React.useState(null);
const disponibles = usePersonasDisponibles();
// La persona elegida se muestra siempre, aunque su voz ya no esté configurada.
const personas = Object.values(VITA_PERSONAS).filter((x) => disponibles.includes(x.id) || x.id === current.id);
// Al elegir una personalidad se reproduce su muestra: la diferencia entre
// Cálida, Serena y Coach está en la VOZ, así que enseñarla sólo por escrito
// deja al paciente eligiendo a ciegas.
const elegir = (personaId) => {
onPick(personaId);
if (!voice || !tts) return;
voice.stopSpeaking();
setSonando(personaId);
voice.speak(
MUESTRA_PERSONA[personaId] || MUESTRA_PERSONA.calida,
VITA_PERSONAS[personaId],
() => setSonando((actual) => (actual === personaId ? null : actual)),
);
};
React.useEffect(() => () => { if (voice) voice.stopSpeaking(); }, []);
const vozNoDisponible = voice && voice.voiceState === 'no_disponible';
return (
¿Cómo quieres que te acompañe?
Elige la voz y el estilo con el que prefieres que te acompañe. Puedes cambiarlo cuando quieras.
{Icon.volume({ s: 15, c: p.primary })}
{vozNoDisponible ? 'La voz no está disponible ahora mismo.' : 'Toca una opción para escucharla.'}
)}
{/* ── Barra de llamada ─────────────────────────────────────────
Sustituye al pulsar-para-hablar. El estado es SIEMPRE visible:
en una conversación por voz, no saber si te están oyendo es peor
que un fallo técnico. */}
{llamada.activa && (