# Vocero React frontend React 19, TypeScript, Tailwind CSS 4, and Vite 8 client for the existing `s2s-client` FastAPI backend. This is a client-side application with React-owned rendering and state, including the realtime WebSocket and audio integration. ## Run during development From the repository root, run `./start_ui.sh`. Open `http://localhost:8420`. Add `?debug=true` to access settings, tools, and custom RAG context. Existing `phoneNumber` and `episode_id` query parameters are supported. The launcher requires Python 3 with venv support, Node.js 22.18 or later on the 22.x line or Node.js 24+, npm, Bash 4.4+, `setsid` (util-linux), and `curl`. It creates `.venv` and installs missing Python dependencies. It runs `npm ci --include=dev` on first start and when either frontend package manifest changes. Subsequent restarts reuse the installed dependencies. | Server | Default address | Purpose | | ------- | ---------------- | ---------------------------------------------- | | Vite | `0.0.0.0:8420` | Public React UI, backend proxy, and hot reload | | FastAPI | `127.0.0.1:8421` | Internal HTTP APIs and speech WebSocket relay | `UI_PORT` preserves the existing public port override. `UI_HOST` defaults to `0.0.0.0`; `S2S_BACKEND_PORT` changes the loopback backend port. The two ports must differ. Occupied ports fail startup rather than silently selecting a new endpoint. The launcher waits for FastAPI before starting Vite, stops both process groups on Ctrl+C/SIGTERM, and exits with a failure if either server exits. Uvicorn watches only `s2s-client`, avoiding frontend and dependency directories. The Vite child explicitly uses `NODE_ENV=development`, including when a process manager sets `NODE_ENV=production`, so React Fast Refresh remains enabled. Vite serves React and Tailwind with hot updates. It proxies `/api`, `/v1/realtime` (including WebSockets), and backend documentation routes to FastAPI. The browser uses one origin on port `8420`, so there is no separate API origin or development CORS setup. The proxy preserves the incoming Host header because FastAPI uses it to construct the speech WebSocket URL. Runtime UI flags, voices, reasoning mode, prompts, and the speech URL still come from FastAPI's `/api/config`. A backend explicitly configured with `SPEECH_TO_SPEECH_URL` continues to return that direct speech endpoint. The launcher sources the root `.env`, retaining its precedence over shell variables. The Vite configuration reads this project's environment files and the inherited `S2S_*` settings. Do not copy the repository's backend secrets into this directory or into `VITE_*` variables, which are exposed to browser code. ## Staging with hot reload PM2 can continue running the root `start_ui.sh` as its existing `s2s_ui` process. Ensure the Node/npm versions above are on PM2's PATH and deploy `s2s-frontend` alongside `s2s-client` and `docs`. The reverse proxy still targets port `8420`. Set the exact public hostname in the root `.env`, without scheme or port: ```dotenv S2S_DEV_ALLOWED_HOSTS=voice-staging.example.com ``` Replace the example with the existing staging hostname; use commas for multiple hosts. IP addresses and localhost are accepted without this setting. The HTTPS reverse proxy must preserve `Host`, `X-Forwarded-Host` and `X-Forwarded-Proto`, and forward WebSocket upgrades for `/v1/realtime` and `/__vite_hmr`. HMR uses the browser's public origin, including HTTPS/WSS, so no second public port is needed. The browser needs HTTPS for microphone access outside localhost. Restart `s2s_ui` after changing launcher or environment settings; source edits use the reloaders. The speech service remains separately managed. ## Run the servers separately Start FastAPI on `127.0.0.1:8421` as described in the [client usage guide](../docs/client/usage.md), then run: ```sh cd s2s-frontend npm ci npm run dev ``` Standalone Vite binds `127.0.0.1:8420`; add `-- --host 0.0.0.0` for remote access. For a different backend, copy `.env.example` to `.env.local` and set `S2S_BACKEND_URL`. The combined launcher overrides that setting to point at the backend it starts. Do not start standalone Vite while the combined launcher already owns the public port. Component and CSS changes use Fast Refresh. Editing the session controller or audio modules can require restarting a conversation or a page reload. Teardown closes microphone tracks, worklets, sockets, queue tickets, timers, and pending requests on session stop or page exit. ## Checks and commands The scripts and code style follow `frontend-admin` conventions, with Vite and React plugins replacing Next.js-specific tooling. Executable configuration files use TypeScript; TypeScript's own project settings use JSON. | Command | Purpose | | ----------------------- | ----------------------------------------------------------- | | `npm run dev` | Vite development server on port 8420, with a fixed port | | `npm run check` | Formatting, strict typed ESLint, type checking, and tests | | `npm run lint:fix` | Apply ESLint fixes | | `npm run format` | Format source and sort Tailwind classes | | `npm run typecheck` | Check application, tests, audio worklets, and configuration | | `npm test` | Run Vitest and React Testing Library regression tests | | `npm run test:watch` | Watch tests while editing | | `npm run test:launcher` | Test launcher supervision and signal handling on Linux | | `npm run build` | Typecheck and compile the app and worklets into `dist/` | | `npm run preview` | Inspect the compiled output locally | No build step is needed before `npm run dev`. `dist/` is generated and ignored by Git. The launcher serves source through Vite; it does not use `dist/` or `vite preview`. Tests cover session initialization, response sequencing, cumulative transcripts, interruptions, queue polling, admission deadlines, cancellation, microphone-permission cancellation, tool results, external reasoning, late-response cleanup, the idle timeout, settings, RAG, profiles, and sanitized Markdown/export behavior. Launcher tests start temporary local children to check readiness, environment forwarding, failures, and shutdown of complete process groups. Vitest handles Node 26's experimental web storage behavior internally. ## Source layout - `src/components/`: voice controls, chat, dialogs, and caller profile views. - `src/realtime/`: session lifecycle, observable application state, queue handling, WebSocket protocol, tools, and external reasoning. - `src/audio/`: TypeScript capture/playback worklets, audio graph, PCM codec, and visualizer. Vite emits the worklets as separate JavaScript assets. - `src/lib/`: validated backend contracts, settings, prompt context, profile and report presentation, Markdown sanitization, and conversation exports. - `config/backend-proxy.ts`: the shared HTTP/WebSocket development proxy. - `src/styles.css`: Tailwind integration and React-specific layout adjustments. - `src/styles/legacy-theme.css`: the existing visual styles, retained for fidelity to both the white microphone and dark orb themes. The UI is ported to React; the full design has not been rewritten as Tailwind utilities. The current Spanish interface, local preference keys, session-only RAG, Markdown replies, JSON/TXT exports, optional browser wake word, TALK/TALKFIRST startup modes, and optional backend features are retained. Camera controls and quick actions remain absent, matching their disabled state in the old UI. Native speech stays at the current project's 16 kHz PCM rate. The backend continues to own VAD, transcription, model inference, synthesis, upstream credentials, and ApiPilot execution. Browser `SpeechSynthesis` is used only when the configured external reasoning service returns an answer. Session startup waits for audio initialization and `session.created`, then sends `session.update` before microphone audio or the initial greeting. The speech server processes those frames in order and does not emit `session.updated` on success. That acknowledgment is therefore optional; receiving it later does not restart the greeting or reset an active response. The startup timeout still closes the microphone and socket if the server never creates a session. ## References - [Vite getting started](https://vite.dev/guide/) - [Vite server and proxy options](https://vite.dev/config/server-options) - [Vite features and asset handling](https://vite.dev/guide/features) - [Tailwind's Vite integration](https://tailwindcss.com/docs/installation/using-vite)