The web UI lives in ui/ and is a React 19 + TypeScript SPA built with Vite
and Bun.
StreamKit uses four complementary state layers:
| Layer | Tool | Purpose |
|---|---|---|
| Per-node session data | Jotai atoms (ui/src/stores/sessionAtoms.ts) |
High-frequency node states, stats, view data, params — per-node atom families confine re-renders to affected components |
| Pipeline structure | Zustand (ui/src/stores/sessionStore.ts) |
Pipeline CRUD, connections, session lifecycle (low-frequency). Also receives node state/stats for transitional compat |
| Atomic UI state | Jotai (ui/src/stores/, various atoms) |
Local UI concerns (selections, toggles, form state) |
| Server state | React Query (@tanstack/react-query) |
REST API data (font/image/audio/plugin assets, session list) |
The WebSocket service (ui/src/services/websocket.ts) batches high-frequency
updates via requestAnimationFrame and writes to Jotai atoms first
(sessionAtoms.ts), with a transitional Zustand write for consumers not yet
migrated. For per-node data (states, stats, params), read from the Jotai atoms.
For pipeline structure and connections, read from the Zustand session store.
Never intermix REST fetches with WebSocket state for the same data. REST
snapshots can be stale relative to WS events (e.g., RuntimeSchemasUpdated
arriving before or after a REST fetchPipeline).
- Functional components with hooks — no class components.
- React.memo for expensive components, especially in the compositor
(see
agent_docs/render-performance.md). - Radix UI primitives for accessible UI components (dialogs, dropdowns, sliders, tabs, tooltips).
- Lucide React for icons.
- Motion (Framer Motion) for animations.
ui/src/
├── components/ UI components (CompositorCanvas, NodeEditor, etc.)
├── hooks/ Custom React hooks (useCompositorLayers, useWebSocket, etc.)
├── views/ Top-level route views (Design, Monitor, Convert, Stream)
├── stores/ Zustand stores and Jotai atoms
├── services/ API clients and WebSocket handler
├── nodes/ React Flow custom node components
├── panes/ Panel/pane components for the editor layout
├── types/ TypeScript type definitions (auto-generated from Rust)
├── constants/ Shared constants
├── utils/ Utility functions
├── test/ Test utilities and perf measurement helpers
└── perf/ Dev-only React.Profiler wrappers
TypeScript types are auto-generated from Rust structs using ts-rs. Run:
just gen-typesThis produces types in ui/src/types/ that match the Rust API contract. Do not
manually edit generated type files.
bun run lint # prettier + eslint + tsc --noEmit
bun run test:run # vitest (all unit tests)
bun run knip # detect unused exports/dependencies
bun run build # production bundleOr via the justfile from the repo root:
just lint-ui # same as bun run lint
just test-ui # same as bun run test:run
just build-ui # production build
just perf-ui # render performance regression tests