# Hesychia (`chat_web_app`) Frontend for **[Hesychia](https://hesychia.ai)** — a React + TypeScript single-page app for a calm AI assistant experience (quiet, rest, stillness). Public product domain: **hesychia.ai** (replacing `chat.aimloperations.com`). Companion backend: [chat_backend](https://git.aimloperations.com/ai_ml_operations/chat_backend) Production serve is **node-static** (not Docker). Hosts build with npm into a document root; the shared **web-static** nginx container from [server-infra](https://git.aimloperations.com/ai_ml_operations/server-infra) serves those assets. Same pattern as `dta_webapp`. Android ships the same CRA `build/` via **Capacitor** (display name **Hesychia**; see `llm-fe/ANDROID.md`). ## Domain migration | Role | Current (still live) | Target | |------|----------------------|--------| | Web UI | `chat.aimloperations.com` | `hesychia.ai` | | REST / WS API | `chatbackend.aimloperations.com` | TBD under hesychia.ai (infra cutover) | | Deploy webroot | `/var/www/{env}.chat.aimloperations/html` | Keep until `server-infra` renames | Committed `.env.production` / `.env.beta` still point at the **current** `*.aimloperations.com` API hosts so builds keep working. Flip those URLs (and deploy webroots / NPM) when DNS and `server-infra` cut over to hesychia.ai. ## Stack - **React 18**, **TypeScript**, **Create React App** (`react-scripts` 5) - **Material UI (MUI) 5** + Emotion for components and theming - **React Router 6** for routing (`BrowserRouter` on web; `HashRouter` under Capacitor) - **Axios** for REST against `chat_backend` (JWT `Authorization` header) - **WebSocket** for live chat (`REACT_APP_BACKEND_WS_API_BASE_URL`) - **Capacitor 7** for Android (and later iOS) WebView shells - **Jest** + React Testing Library for unit tests ## Repository layout ``` chat_web_app/ ├── README.md ├── .gitea/workflows/ # Unit tests + deploy-prod / deploy-beta └── llm-fe/ # CRA + Capacitor npm root ├── package.json ├── capacitor.config.ts ├── ANDROID.md # Android build, signing, Play Store notes ├── android/ # Native Android project (committed) ├── assets/ # Source icon/splash for @capacitor/assets ├── .env.development # Local dev API/WS URLs ├── .env.beta ├── .env.production ├── .env.mobile # Capacitor shell API/WS URLs ├── public/ └── src/ ├── axiosApi.js # Axios instances (JWT Authorization) ├── App.tsx ├── index.tsx └── llm-fe/ ├── pages/ # Route-level views ├── components/ # Shared & feature UI ├── contexts/ # Auth, account, WebSocket, theme ├── platform/ # Capacitor routing / native chrome └── ui-kit/ # MD* design-system wrappers ``` ## Prerequisites - **Node.js 20** (matches CI) - **npm** (lockfile: `llm-fe/package-lock.json`) - Running [chat_backend](https://git.aimloperations.com/ai_ml_operations/chat_backend) locally for full chat/auth (default in `.env.development`: port **8001**) - For Android: JDK 17+, Android Studio / SDK (see `llm-fe/ANDROID.md`) ## Local setup ### 1. Clone and install ```bash git clone ssh://git@git.aimloperations.com:30009/ai_ml_operations/chat_web_app.git cd chat_web_app/llm-fe npm ci ``` ### 2. Configure environment CRA loads mode-specific env files. Variables must be prefixed with `REACT_APP_` to be visible in the browser bundle. | File | Used when | Backend (current) | |------|-----------|-------------------| | `.env.development` | `npm start` | `http://127.0.0.1:8001` | | `.env.production` | `npm run build` / `build:prod` | `https://chatbackend.aimloperations.com` | | `.env.beta` | `npm run build:beta` | `https://beta.chatbackend.aimloperations.com` | | `.env.mobile` | `npm run build:mobile` | prod by default (override for beta shell) | Required keys (already set in the committed env files): ```env REACT_APP_BACKEND_REST_API_BASE_URL=http://127.0.0.1:8001/api/ REACT_APP_BACKEND_WS_API_BASE_URL=ws://127.0.0.1:8001/ws/chat_again/ ``` These are **baked into the JS at build time**. There is no host secret file for this frontend (unlike Django apps under `~/Documents/secrets/`). Change the committed `.env.*` files if API domains change, then redeploy. Optional local overrides (gitignored): `.env.local`, `.env.development.local`, `.env.production.local`, `.env.mobile.local`. ### 3. Start the backend From the `chat_backend` repo (adjust to however that service is run locally): ```bash # example — follow chat_backend README for the current command cd chat_backend # run Django on the port matching .env.development (8001) ``` ### 4. Start the dev server ```bash cd llm-fe npm start ``` Open **http://localhost:3000**. ### 5. Android (Capacitor) ```bash cd llm-fe npm run build:mobile npm run android:open ``` Full signing / Play Store notes: [`llm-fe/ANDROID.md`](llm-fe/ANDROID.md). ## npm scripts | Command | Purpose | |---------|---------| | `npm start` | CRA dev server (port 3000) | | `npm run build` | Production build into `llm-fe/build/` | | `npm run build:prod` | Prod build + copy to `/var/www/prod.chat.aimloperations/html` | | `npm run build:beta` | Beta env build + copy to `/var/www/beta.chat.aimloperations/html` | | `npm run build:mobile` | Mobile env build + `cap sync android` | | `npm run android:open` | Open `android/` in Android Studio | | `npm run android:sync` | `cap sync android` only | | `npm run assets:generate` | Regenerate Android icons/splash from `assets/` | | `npm test` | Jest watch mode | | `npm run test:ci` | Single CI run (`CI=true`, no watch) | `build:prod` / `build:beta` are intended for deploy hosts (or a machine that can write those `/var/www/...` paths). Paths must match `server-infra` `app_catalog.chat_web_app.webroot_pattern` until renamed: ```text /var/www/{env}.chat.aimloperations/html ``` ## Tests ```bash cd llm-fe npm test # watch npm run test:ci # one-shot (matches Gitea Unit Tests) ``` ## CI / deployment Gitea Actions workflows in `.gitea/workflows/`: | Workflow | Trigger | Behavior | |----------|---------|----------| | `unit-tests.yml` | push/PR to `master` | `npm ci` + `npm run test:ci` in `llm-fe/` | | `deploy-prod.yml` | after green **Unit Tests** on `master` **push** | `server-infra/scripts/deploy.sh --app chat_web_app --env prod --ref ` | | `deploy-beta.yml` | `workflow_dispatch` | tests, then deploy `--env beta` | Deploy flow (on each webserver listed in `host_apps`): 1. Git checkout at the pinned ref under `/opt/apps/src/chat_web_app_` 2. `npm ci` in `llm-fe/` 3. `npm run build:` → writes the static tree under `/var/www/...` 4. **web-static** nginx serves that root on the host port (prod **8082**, beta **8083** if enabled) Manual deploy from the control node: ```bash ~/Documents/repos/server-infra/scripts/deploy.sh \ --app chat_web_app --env prod --ref ``` NPM (not managed by Ansible) should balance public Hesychia domains to: - `adama:8082` + `roslin:8082` + `ai-server-4080:8082` (prod) See [server-infra IMPLEMENTATION.md](https://git.aimloperations.com/ai_ml_operations/server-infra). ## Related repos - **Backend:** [chat_backend](https://git.aimloperations.com/ai_ml_operations/chat_backend) — Django API + WebSocket - **Infra:** [server-infra](https://git.aimloperations.com/ai_ml_operations/server-infra) — `app_catalog`, `host_apps`, `deploy.sh`, web-static - **Pattern reference:** [dta_webapp](https://git.aimloperations.com/Ditch_The_Agent/dta_webapp) — node-static frontend deploy