Real-time heads-up display for live qualitative research interviews. Streams live transcript, surfaces AI-generated prompt suggestions, tracks tags, signals, and session context in a single React interface.
- React 19 + TypeScript (strict)
- Vite 8 bundler, vitest test runner
- MUI v9 component library
- IndexedDB for local session persistence
- WebSocket real-time transcript stream
- PWA (vite-plugin-pwa, offline-capable)
- Node.js 22+
- npm 10+
npm ciCopy the example file and fill in your values:
cp .env.example .env.env.example:
# REST API base URL (no trailing slash)
VITE_HUD_API_URL=http://localhost:3000/api/v1
# WebSocket URL for the real-time transcript stream
VITE_TRANSCRIPT_WS_URL=ws://localhost:3000/ws/transcriptFor production, point both vars at your deployed server:
VITE_HUD_API_URL=https://api.your-domain.com/api/v1
VITE_TRANSCRIPT_WS_URL=wss://api.your-domain.com/ws/transcriptVite embeds
VITE_*variables at build time. Set them in your CI/CD environment or Vercel dashboard before building — they cannot be changed at runtime.
npm run devApp runs at http://localhost:5173 by default.
npm test # single run
npm run test:watch # watch mode
npm run test:coverage # with coverage reportnpm run buildOutput goes to dist/. Preview the production build locally:
npm run previewFor PWA install testing, prefer npm run build && npm run preview (the dev server often skips or differs from production PWA behavior).
-
Push your code to GitHub, GitLab, or Bitbucket.
-
Go to vercel.com/new and import the repository.
-
Vercel auto-detects Vite. Confirm the settings:
- Framework preset: Vite
- Build command:
npm run build - Output directory:
dist - Root directory:
client(if the repo root is the monorepo root)
-
Add environment variables under Settings → Environment Variables:
Name Value VITE_HUD_API_URLhttps://api.your-domain.com/api/v1VITE_TRANSCRIPT_WS_URLwss://api.your-domain.com/ws/transcript -
Click Deploy. Every push to
maintriggers a new production deploy automatically.
npm i -g vercel
vercel login
vercel --prodThe CLI prompts you to link a project and configure env vars interactively.
vercel.json (already committed) configures the /* rewrite to index.html so client-side routes like /login work after a hard reload.
vercel.json also sets:
X-Content-Type-Options: nosniffX-Frame-Options: DENYReferrer-Policy: strict-origin-when-cross-originPermissions-Policy: microphone=(self)— required for the mic capture featureCache-Control: immutableon/assets/*(hashed filenames, safe to cache forever)
src/
├── app/ # Router, providers, ErrorBoundary
├── layouts/ # DashboardLayout
├── modules/
│ ├── auth/ # Login, AuthProvider, token management
│ ├── context/ # Session store (metadata, notes, tags)
│ ├── prompts/ # AI prompt suggestions
│ ├── tagging/ # Tag panel + keyboard shortcuts
│ ├── timeline/ # Event timeline markers
│ └── transcript/ # WebSocket stream, mic capture, transcript UI
└── shared/
├── constants/ # DESKTOP_SENTINEL and other shared literals
├── services/ # IndexedDB session persistence
├── types/ # Shared TypeScript types
└── utils/ # fetchWithAuth, hudApi, formatters
| Variable | Required | Default (dev) | Notes |
|---|---|---|---|
VITE_HUD_API_URL |
Yes | http://localhost:3000/api/v1 |
REST API base |
VITE_TRANSCRIPT_WS_URL |
Yes | ws://localhost:3000/ws/transcript |
WebSocket endpoint |
Both variables default to localhost:3000 when absent, so the app builds and runs in development without a .env file. Production deployments must set them explicitly — there is no runtime fallback to a production URL.
- Access tokens are kept in React state only (never persisted to storage).
- Refresh tokens are stored in
sessionStorage(cleared on tab close). Long-term goal: migrate tohttpOnlycookies (requires server coordination). - No secrets, API keys, or credentials should ever be committed. The
.gitignoreexcludes all.env*files except.env.example.