import { BookOpen, ChartNoAxesColumn, ChevronRight, Info, PanelLeft, Settings, Wrench, X } from "lucide-react"; import { useEffect, useLayoutEffect, useState, useSyncExternalStore } from "react"; import { Bubbles } from "./components/Bubbles"; import { Conversation } from "./components/Conversation"; import { AboutDialog, ProfileDialog, RagDialog, SettingsDialog, ToolsDialog } from "./components/SettingsDialogs"; import { VoiceStage } from "./components/VoiceStage"; import { useWakeWord } from "./hooks/use-wake-word"; import { initials, profileName } from "./lib/context"; import { reportSuggestions } from "./lib/reports"; import { isDebug } from "./lib/settings"; import { ACTIVE_STATES, VoiceController } from "./realtime/controller"; type Modal = "settings" | "tools" | "rag" | "profile" | "about" | null; export default function App() { const [controller] = useState(() => new VoiceController()); const state = useSyncExternalStore(controller.subscribe, controller.getSnapshot); const [modal, setModal] = useState(null); const [reportsOpen, setReportsOpen] = useState(false); const debug = isDebug(); const white = state.config.theme === "white"; const name = profileName(state.profile); const hasReports = debug && !white && !!state.reports?.count; useEffect(() => { const abort = new AbortController(); void controller.initialize(abort.signal); const leave = () => { void controller.stop(); }; window.addEventListener("pagehide", leave); return () => { abort.abort(); window.removeEventListener("pagehide", leave); void controller.stop(); }; }, [controller]); useLayoutEffect(() => { if (!state.loading) { document.documentElement.dataset.theme = white ? "white" : "dark"; } }, [state.loading, white]); useEffect(() => { if (state.loading) { return; } const timer = setTimeout(() => { controller.offerAutoStart(); }, 2000); return () => { clearTimeout(timer); }; }, [state.loading, controller]); useWakeWord(!state.loading && !ACTIVE_STATES.has(state.status) && !modal && !state.unlock, controller); useEffect(() => { if (!reportsOpen) { return; } const escape = (event: KeyboardEvent) => { if (event.key === "Escape") { setReportsOpen(false); } }; window.addEventListener("keydown", escape); return () => { window.removeEventListener("keydown", escape); }; }, [reportsOpen]); const close = () => { setModal(null); }; if (state.loading) { return (
Preparando Vocero…
); } return ( <> {state.unlock && ( )}

Vocero

{white &&

Agente conversacional

}
{white && name && ( )} {hasReports && ( )} {debug && (
)}
{white && (

Listo para conversar

Informes, análisis, datos y mucho más.

)} {state.error && (
{state.error}
)}
{!white && ACTIVE_STATES.has(state.status) && } {hasReports && ( )} {modal === "settings" && } {modal === "tools" && } {modal === "rag" && } {modal === "profile" && } {modal === "about" && } ); }