wealth-alpha-chat-widget 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/DEPLOY.md ADDED
@@ -0,0 +1,283 @@
1
+ # Deployment guide
2
+
3
+ Production rollout for the full stack:
4
+ - **Backend** — FastAPI + `chat_widget.py` + `chat_formatters.py`
5
+ - **Library** — published to npm
6
+ - **Frontend** — Next.js consuming the library
7
+
8
+ ---
9
+
10
+ ## 1. Backend (`WealthAlpha-Backend`)
11
+
12
+ ### 1.1 Files that must ship
13
+
14
+ The chat router introduces two new files on top of the team's existing backend:
15
+
16
+ ```
17
+ app/api_v1/chat_widget.py ← chip router, multi-step state, intent routing
18
+ app/api_v1/chat_formatters.py ← 18 template formatters
19
+ ```
20
+
21
+ And `app/api_v1/router.py` must include the widget:
22
+
23
+ ```python
24
+ from app.api_v1 import ..., chat_widget
25
+
26
+ api_v1_router.include_router(
27
+ chat_widget.chat_widget_router,
28
+ prefix="/chat-widget",
29
+ tags=["chat-widget"],
30
+ )
31
+ ```
32
+
33
+ ### 1.2 Dependencies
34
+
35
+ `requirements.txt` already includes `httpx`, `fastapi`, `pydantic`, `asyncpg`. No extra dependencies needed for the chat widget — `chat_widget.py` reuses what's there.
36
+
37
+ ### 1.3 Environment variables
38
+
39
+ Reuses the team's existing env vars (`SECRET_KEY`, `DATABASE_URL`, etc.). The widget doesn't introduce new env vars.
40
+
41
+ ### 1.4 Run with uvicorn (single instance)
42
+
43
+ ```bash
44
+ cd WealthAlpha-Backend
45
+ source venv/bin/activate
46
+ pip install -r requirements.txt
47
+
48
+ uvicorn main:app \
49
+ --host 0.0.0.0 \
50
+ --port 8013 \
51
+ --proxy-headers \
52
+ --forwarded-allow-ips='*' \
53
+ --workers 4
54
+ ```
55
+
56
+ - `--proxy-headers` — trusts `X-Forwarded-Proto`/`X-Forwarded-Host` from the reverse proxy so `_base_url_from_request()` returns the correct `https://` URL for the httpx loopback.
57
+ - `--forwarded-allow-ips='*'` — accepts forwarded headers from any upstream proxy IP. Lock down to your specific reverse proxy IP in production.
58
+ - `--workers 4` — fork worker processes. Each has its own `_SessionStore` dict (see §1.6).
59
+
60
+ ### 1.5 Reverse proxy (Nginx)
61
+
62
+ ```nginx
63
+ upstream wealth_backend {
64
+ server 127.0.0.1:8013;
65
+ }
66
+
67
+ server {
68
+ server_name api.your-domain.com;
69
+ listen 443 ssl http2;
70
+
71
+ location /api/v1/chat-widget/ {
72
+ proxy_pass http://wealth_backend;
73
+ proxy_set_header Host $host;
74
+ proxy_set_header X-Forwarded-Proto $scheme;
75
+ proxy_set_header X-Forwarded-Host $host;
76
+ proxy_set_header X-Real-IP $remote_addr;
77
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
78
+ proxy_read_timeout 60s;
79
+ }
80
+
81
+ # All other /api/v1/* routes
82
+ location /api/v1/ {
83
+ proxy_pass http://wealth_backend;
84
+ proxy_set_header Host $host;
85
+ proxy_set_header X-Forwarded-Proto $scheme;
86
+ proxy_set_header X-Forwarded-Host $host;
87
+ proxy_set_header X-Real-IP $remote_addr;
88
+ }
89
+ }
90
+ ```
91
+
92
+ ### 1.6 Scaling beyond one process
93
+
94
+ `chat_widget.py` uses an **in-memory `_SessionStore` dict** for multi-step state (`{action, collected, awaiting}` per `sessionId`). This is single-process only.
95
+
96
+ For multi-worker / multi-instance deploys, swap `_SessionStore` for a Redis-backed implementation:
97
+
98
+ ```python
99
+ import redis.asyncio as redis
100
+
101
+ class _SessionStore:
102
+ _TTL_SECONDS = 1800
103
+ def __init__(self):
104
+ self._r = redis.from_url(os.environ["REDIS_URL"])
105
+
106
+ async def get(self, session_id):
107
+ raw = await self._r.get(f"wac:{session_id}")
108
+ return json.loads(raw) if raw else None
109
+
110
+ async def set(self, session_id, action, collected, awaiting):
111
+ await self._r.setex(
112
+ f"wac:{session_id}",
113
+ self._TTL_SECONDS,
114
+ json.dumps({"action": action, "collected": collected, "awaiting": awaiting}),
115
+ )
116
+
117
+ async def clear(self, session_id):
118
+ await self._r.delete(f"wac:{session_id}")
119
+ ```
120
+
121
+ Update all `sessions.get(...)` / `sessions.set(...)` / `sessions.clear(...)` calls to `await ...`. The TTL stays at 30 min.
122
+
123
+ ### 1.7 Systemd unit (optional)
124
+
125
+ ```ini
126
+ # /etc/systemd/system/wealth-alpha-backend.service
127
+ [Unit]
128
+ Description=Wealth Alpha FastAPI
129
+ After=network.target postgresql.service redis.service
130
+
131
+ [Service]
132
+ WorkingDirectory=/srv/wealth-alpha/backend
133
+ ExecStart=/srv/wealth-alpha/backend/venv/bin/uvicorn main:app \
134
+ --host 0.0.0.0 --port 8013 \
135
+ --proxy-headers --forwarded-allow-ips='*' \
136
+ --workers 4
137
+ EnvironmentFile=/etc/wealth-alpha/backend.env
138
+ Restart=always
139
+ User=wealth
140
+ Group=wealth
141
+
142
+ [Install]
143
+ WantedBy=multi-user.target
144
+ ```
145
+
146
+ ```bash
147
+ sudo systemctl daemon-reload
148
+ sudo systemctl enable wealth-alpha-backend
149
+ sudo systemctl start wealth-alpha-backend
150
+ sudo journalctl -u wealth-alpha-backend -f # tail logs
151
+ ```
152
+
153
+ ---
154
+
155
+ ## 2. Library (npm)
156
+
157
+ See **[PUBLISH.md](./PUBLISH.md)** — full release flow.
158
+
159
+ Short version for an existing published library:
160
+
161
+ ```bash
162
+ cd Wealth-alpha-chat-UI
163
+ npm version patch # or minor / major
164
+ npm run build
165
+ npm publish --access public
166
+ git push --follow-tags
167
+ ```
168
+
169
+ ---
170
+
171
+ ## 3. Frontend (Next.js host app)
172
+
173
+ ### 3.1 Install the published library
174
+
175
+ ```bash
176
+ cd WealthAlpha-Frontend
177
+ npm install wealth-alpha-chat@latest
178
+ ```
179
+
180
+ ### 3.2 Environment variables
181
+
182
+ In `.env.production` (or your platform's dashboard — Vercel, Netlify, etc.):
183
+
184
+ ```env
185
+ NEXT_PUBLIC_API_BASE=https://api.your-domain.com/api/v1/chat-widget
186
+ ```
187
+
188
+ The library uses this as `apiBase`. No other env vars are required by the widget itself.
189
+
190
+ ### 3.3 Build & deploy
191
+
192
+ #### Vercel
193
+
194
+ ```bash
195
+ vercel --prod
196
+ ```
197
+
198
+ Vercel auto-detects Next.js, builds with `next build`, and serves the static + serverless output.
199
+
200
+ #### Self-hosted
201
+
202
+ ```bash
203
+ cd WealthAlpha-Frontend
204
+ npm ci
205
+ npm run build
206
+ npm run start # serves on PORT (default 3000)
207
+ ```
208
+
209
+ Behind Nginx:
210
+
211
+ ```nginx
212
+ server {
213
+ server_name app.your-domain.com;
214
+ listen 443 ssl http2;
215
+
216
+ location / {
217
+ proxy_pass http://127.0.0.1:3000;
218
+ proxy_set_header Host $host;
219
+ proxy_set_header X-Real-IP $remote_addr;
220
+ proxy_set_header X-Forwarded-Proto $scheme;
221
+ }
222
+ }
223
+ ```
224
+
225
+ ---
226
+
227
+ ## 4. End-to-end smoke test after deploy
228
+
229
+ ```bash
230
+ # 1. Backend up?
231
+ curl -s https://api.your-domain.com/api/v1/chat-widget/me
232
+ # → {"detail":"Not authenticated"} ✓ route exists, auth-gated
233
+
234
+ # 2. Frontend serves the bundled library?
235
+ curl -s https://app.your-domain.com/ | grep -oE "wealth-alpha-chat" | head -1
236
+ # → some occurrence of the package name in the HTML or chunk references
237
+
238
+ # 3. CORS allows your frontend domain?
239
+ curl -s -I -X OPTIONS https://api.your-domain.com/api/v1/chat-widget/me \
240
+ -H "Origin: https://app.your-domain.com" \
241
+ -H "Access-Control-Request-Method: GET"
242
+ # → Access-Control-Allow-Origin: * (or your specific origin)
243
+
244
+ # 4. Open https://app.your-domain.com in browser
245
+ # 5. Log in normally
246
+ # 6. Click 💬 → confirm welcome with your name + chips
247
+ # 7. Click any chip → confirm result renders with bold/italic/bullets
248
+ ```
249
+
250
+ ---
251
+
252
+ ## 5. Common production issues
253
+
254
+ | Symptom | Cause | Fix |
255
+ |---|---|---|
256
+ | `httpx.ConnectError: SSL` when chat calls upstream | Backend behind reverse proxy but `--proxy-headers` not set | Add `--proxy-headers --forwarded-allow-ips='*'` to uvicorn |
257
+ | `401` on `/chat-widget/me` for all users | Different JWT secret in prod vs the one issuing tokens | Verify `SECRET_KEY` env var matches across services |
258
+ | `403 premium required` on every chip | `telegram_id` not linked for users | Check that `users.telegram_id` is populated. Widget extracts from `current_user.telegram_id`. |
259
+ | Multi-step flow loses state between requests | Multiple uvicorn workers, in-memory `_SessionStore` | Swap to Redis-backed store (§1.6) |
260
+ | Chat history endpoint returns nothing | `ChatService` DB writes failing silently | Check uvicorn logs for "chat_widget: persist_turn failed" errors |
261
+ | Bold rendering doesn't work | Library cached at CDN | Bust CDN cache; verify `wealth-alpha-chat` version in `node_modules` is current |
262
+
263
+ ---
264
+
265
+ ## 6. Monitoring
266
+
267
+ Recommended log lines to alert on:
268
+
269
+ - `chat_widget: persist_turn failed` — DB write failed
270
+ - `upstream … unreachable` — telegram-api endpoint down
271
+ - `intent unavailable for user=` — intent classifier down
272
+ - HTTP 5xx on `/chat-widget/*` — chat router crashed
273
+
274
+ `logger = logging.getLogger("wealth_alpha.chat_widget")` namespace is used throughout. Pipe it to your aggregator (Datadog, Loki, CloudWatch).
275
+
276
+ For per-request tracing, the library sends `X-Request-Id` header on every chat call. Match it in backend logs:
277
+
278
+ ```python
279
+ # In chat_widget.py — add at the top of each endpoint
280
+ logger.info("chat_widget %s %s req_id=%s",
281
+ request.method, request.url.path,
282
+ request.headers.get("x-request-id"))
283
+ ```
@@ -0,0 +1,202 @@
1
+ # Publishing `wealth-alpha-chat` to npm
2
+
3
+ How to cut a new version of the library, build it, and push it to the npm registry.
4
+
5
+ ---
6
+
7
+ ## 1. Pre-flight checklist
8
+
9
+ Run before every publish. Any failure → fix before continuing.
10
+
11
+ ```bash
12
+ # 1. Clean working tree
13
+ git status
14
+ # (should show nothing besides files you intend to publish)
15
+
16
+ # 2. Typecheck clean
17
+ npm run typecheck
18
+
19
+ # 3. Build produces dist/ with ESM + CJS + types + CSS
20
+ npm run build
21
+ ls -la dist/
22
+ # Expect: index.mjs, index.cjs, index.d.ts, index.d.cts, index.css
23
+
24
+ # 4. "use client" directive is preserved at top of both JS outputs (required for Next.js App Router)
25
+ head -1 dist/index.mjs # "use client";
26
+ head -1 dist/index.cjs # "use client";
27
+
28
+ # 5. Bundle size sanity check (< 60 KB total uncompressed is healthy)
29
+ du -sh dist/
30
+ ```
31
+
32
+ ---
33
+
34
+ ## 2. Version bump
35
+
36
+ Follow [semver](https://semver.org/):
37
+
38
+ | Change | Bump |
39
+ |---|---|
40
+ | Bug fix, no API change | patch (0.1.0 → 0.1.1) |
41
+ | New prop / new export / chip tree extension | minor (0.1.0 → 0.2.0) |
42
+ | Removed/renamed prop, breaking chat-widget protocol | major (0.1.0 → 1.0.0) |
43
+
44
+ ```bash
45
+ npm version patch # → 0.1.1
46
+ npm version minor # → 0.2.0
47
+ npm version major # → 1.0.0
48
+ ```
49
+
50
+ This automatically updates `package.json` and creates a `vX.Y.Z` git tag.
51
+
52
+ ---
53
+
54
+ ## 3. Dry-run with `npm pack`
55
+
56
+ Always pack first to inspect what npm will publish:
57
+
58
+ ```bash
59
+ npm pack
60
+ # → wealth-alpha-chat-0.1.0.tgz
61
+
62
+ tar -tzf wealth-alpha-chat-0.1.0.tgz | head -30
63
+ ```
64
+
65
+ The tarball should contain:
66
+ - `package/package.json`
67
+ - `package/README.md`
68
+ - `package/dist/index.mjs`, `dist/index.cjs`, `dist/index.d.ts`, `dist/index.d.cts`, `dist/index.css`
69
+ - `dist/*.map` (source maps)
70
+
71
+ It should **NOT** contain:
72
+ - `src/` (we ship compiled output only — `.npmignore` blocks it)
73
+ - `node_modules/`
74
+ - `examples/`
75
+ - `*.config.ts`, `tsconfig.json`
76
+ - internal docs `WEALTH_ALPHA_CHAT_PLAN.md`, `api_response.md`
77
+
78
+ ---
79
+
80
+ ## 4. Test the tarball locally in a fresh project
81
+
82
+ Before pushing to npm, sanity-check by installing the tarball into a real consumer:
83
+
84
+ ```bash
85
+ cd ~/test-project
86
+ npm install /abs/path/to/wealth-alpha-chat-0.1.0.tgz --force
87
+ npm run dev
88
+ # manually click through chat flows
89
+ ```
90
+
91
+ ---
92
+
93
+ ## 5. Login to npm
94
+
95
+ First time only:
96
+
97
+ ```bash
98
+ npm login
99
+ # Enter username, password, email, OTP
100
+ ```
101
+
102
+ Verify:
103
+
104
+ ```bash
105
+ npm whoami
106
+ # → your-npm-username
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 6. Publish
112
+
113
+ ```bash
114
+ npm publish --access public
115
+ ```
116
+
117
+ `--access public` is required for scoped packages and any package on a new account. For an unscoped public package it's still safe to include.
118
+
119
+ You should see:
120
+
121
+ ```
122
+ + wealth-alpha-chat@0.1.0
123
+ ```
124
+
125
+ Confirm on npmjs.com:
126
+
127
+ ```bash
128
+ npm view wealth-alpha-chat
129
+ ```
130
+
131
+ ---
132
+
133
+ ## 7. Push the git tag
134
+
135
+ ```bash
136
+ git push --follow-tags
137
+ ```
138
+
139
+ This pushes both the version bump commit and the `vX.Y.Z` tag.
140
+
141
+ ---
142
+
143
+ ## 8. Post-publish smoke test
144
+
145
+ Spin up an empty Next.js project, install from npm (not the tarball), and verify the widget mounts:
146
+
147
+ ```bash
148
+ npx create-next-app@latest test-app --typescript --app
149
+ cd test-app
150
+ npm install wealth-alpha-chat
151
+ # Add <WealthChat /> to src/app/layout.tsx per SETUP.md
152
+ npm run dev
153
+ # Open http://localhost:3000, confirm 💬 appears, click → AuthGate renders
154
+ ```
155
+
156
+ ---
157
+
158
+ ## 9. Handling failures
159
+
160
+ | Failure | Fix |
161
+ |---|---|
162
+ | `403 Forbidden — version already exists` | Bump the version (`npm version patch`) and try again — npm doesn't allow republishing the same version. |
163
+ | `EPUBLISHCONFLICT` | Same as above. |
164
+ | `EINVALIDTAGNAME` | `npm version` couldn't create the git tag — usually because the working tree has uncommitted changes. Commit or stash, retry. |
165
+ | `ENEEDAUTH` | Re-run `npm login`. |
166
+ | `Bundle size unexpectedly grew` | Check `package.json` `dependencies` — only `markdown-it` should be there. React/React-DOM must stay in `peerDependencies`. |
167
+ | `Next.js App Router consumers see "Cannot use React Hook"` | The `"use client"` directive got stripped. Re-run `npm run build` and verify with `head -1 dist/index.mjs` that the directive is still there. The `scripts/add-use-client.mjs` post-build script re-injects it. |
168
+
169
+ ---
170
+
171
+ ## 10. Rollback / deprecate
172
+
173
+ If a version is broken in the wild:
174
+
175
+ ```bash
176
+ # Mark a version as deprecated (doesn't unpublish, but warns on install)
177
+ npm deprecate wealth-alpha-chat@0.1.0 "Use 0.1.1 instead — fixes critical chip-routing bug"
178
+
179
+ # Within 72 hours of publish, you CAN unpublish
180
+ npm unpublish wealth-alpha-chat@0.1.0
181
+ ```
182
+
183
+ After 72 hours npm doesn't allow unpublish. Always prefer deprecate + push a patch.
184
+
185
+ ---
186
+
187
+ ## 11. Release notes
188
+
189
+ Write release notes in `CHANGELOG.md` (one section per version) and tag the GitHub release with them:
190
+
191
+ ```md
192
+ ## 0.1.1 — 2026-05-28
193
+
194
+ ### Fixed
195
+ - Chip path no longer doubles when apiBase ends with `/chat-widget`.
196
+ - Library reads `localStorage["token"]` as a fallback when `wac_session` is empty.
197
+
198
+ ### Added
199
+ - markdown-it rendering for bot bubbles (bold, italic, bullets, links).
200
+ - Drill-down chips on list results (Smart Money Picks, Discovery, Crypto List, Peer).
201
+ - Telegram-style chip flows for Stock Analysis, Portfolio Risk, Crypto, Market Forecast.
202
+ ```
package/docs/SETUP.md ADDED
@@ -0,0 +1,180 @@
1
+ # Setup guide — integrating `wealth-alpha-chat` into a host app
2
+
3
+ This is what a consumer (React or Next.js project) needs to do to mount the chat widget.
4
+
5
+ ---
6
+
7
+ ## 1. Install
8
+
9
+ ```bash
10
+ npm install wealth-alpha-chat
11
+ # or
12
+ yarn add wealth-alpha-chat
13
+ # or
14
+ pnpm add wealth-alpha-chat
15
+ ```
16
+
17
+ **Peer requirements**: React ≥ 17, React DOM ≥ 17. No other peers.
18
+
19
+ ---
20
+
21
+ ## 2. Import the CSS once
22
+
23
+ The library ships scoped CSS Modules, but the consumer must include the stylesheet once globally.
24
+
25
+ ### Next.js App Router (`src/app/layout.tsx`)
26
+
27
+ ```tsx
28
+ import "wealth-alpha-chat/styles.css";
29
+ ```
30
+
31
+ ### Vite / CRA (`src/main.tsx` or `src/index.tsx`)
32
+
33
+ ```tsx
34
+ import "wealth-alpha-chat/styles.css";
35
+ ```
36
+
37
+ ---
38
+
39
+ ## 3. Mount the widget
40
+
41
+ Add `<WealthChat />` somewhere it stays mounted on every page — typically the root layout, outside the page content. The widget renders a floating button that opens a chat panel.
42
+
43
+ ```tsx
44
+ import { WealthChat } from "wealth-alpha-chat";
45
+
46
+ <WealthChat
47
+ apiBase="https://api.your-domain.com/api/v1/chat-widget"
48
+ authCheck="/me"
49
+ loginUrl="/login"
50
+ sessionTTL={1800}
51
+ brandName="Wealth Alpha AI"
52
+ brandColor="#1a2d5a"
53
+ position="bottom-right"
54
+ />
55
+ ```
56
+
57
+ ### Props
58
+
59
+ | Prop | Type | Required | Default | Purpose |
60
+ |---|---|---|---|---|
61
+ | `apiBase` | string | ✅ | — | Base URL of the chat-widget backend. Library appends `/me`, `/chip/{id}`, `/message`. **Do not include `/chat/`** in the suffix or the path doubles. |
62
+ | `authCheck` | string | ❌ | `"/me"` | Path appended to `apiBase` for the auth check. |
63
+ | `loginUrl` | string | ✅ | — | Where the AuthGate's "Sign in" button redirects. |
64
+ | `sessionTTL` | number (seconds) | ❌ | `1800` | Sliding-window session expiry. Resets on each chip/message. |
65
+ | `welcomeMessage` | string | ❌ | uses backend's `/me` `welcomeMessage` | Override the welcome bubble text. |
66
+ | `brandName` | string | ❌ | `"Wealth Alpha AI"` | Shown in the chat header and AuthGate. |
67
+ | `brandColor` | string (CSS color) | ❌ | `"#1a2d5a"` | Header background, floating button, primary buttons. Exposed as `--wac-brand` CSS variable. |
68
+ | `position` | `"bottom-right"` \| `"bottom-left"` | ❌ | `"bottom-right"` | Floating button + panel anchor. |
69
+ | `defaultOpen` | boolean | ❌ | `false` | Open the chat panel on mount instead of just showing the floating button. |
70
+ | `showCountdown` | boolean | ❌ | `true` | Show "Session: 23m left" in the header. |
71
+ | `onLogin` | `() => void` | ❌ | — | Fires when the chat detects a valid JWT and shows the welcome message. |
72
+ | `onLogout` | `() => void` | ❌ | — | Fires when the session is cleared. |
73
+ | `onSessionExpire` | `() => void` | ❌ | — | Fires when the sliding-window TTL expires. |
74
+ | `onError` | `(err: Error) => void` | ❌ | — | Fires on auth or network errors. |
75
+
76
+ ---
77
+
78
+ ## 4. Authentication — automatic JWT pickup
79
+
80
+ The library auto-discovers a JWT from `localStorage`. It checks these keys in order:
81
+
82
+ 1. `wac_session.token` (full session object — set by the library itself or a bridge)
83
+ 2. `token` ← **your normal login key**
84
+ 3. `access_token`
85
+ 4. `auth_token`
86
+ 5. `jwt`
87
+
88
+ So if your existing login already stores the JWT under `localStorage["token"]`, **no bridge code is needed**. The widget will pick it up on next render or `storage` event.
89
+
90
+ ### Auto-logout
91
+
92
+ When the chat adopts a JWT from a fallback key it stamps the session with `bootSource: "fallback"`. On every read, if the fallback key is gone, the cached `wac_session` is cleared too — so when the host app calls `localStorage.removeItem("token")` on logout, the chat falls back to the AuthGate within one tick. No `onLogout` plumbing required.
93
+
94
+ If you set `wac_session` directly (not via a fallback key), the auto-logout doesn't apply — you control its lifecycle.
95
+
96
+ ### What if my token is in a different key?
97
+
98
+ Either rename your storage key to one of the above, or set `wac_session` directly:
99
+
100
+ ```ts
101
+ localStorage.setItem("wac_session", JSON.stringify({
102
+ token: yourJwtString,
103
+ userId: "u_xxx",
104
+ sessionId: crypto.randomUUID(),
105
+ expiresAt: Date.now() + 30 * 60 * 1000,
106
+ lastActive: Date.now(),
107
+ history: [],
108
+ }));
109
+ ```
110
+
111
+ ### What if my token is in an httpOnly cookie?
112
+
113
+ The library only reads JWTs from `localStorage`. For cookie-based auth, you need to either:
114
+ - Set `credentials: "include"` in your fetch wrapper (would require library change), or
115
+ - Expose a small endpoint that returns the JWT for the logged-in user and write it to `localStorage` on login.
116
+
117
+ ---
118
+
119
+ ## 5. Environment variables
120
+
121
+ | Variable | Where | Purpose |
122
+ |---|---|---|
123
+ | `NEXT_PUBLIC_API_BASE` | Next.js host app | Override the default `apiBase`. Recommended pattern: `process.env.NEXT_PUBLIC_API_BASE ?? "http://localhost:8013/api/v1/chat-widget"` |
124
+
125
+ You can also wire `brandColor`, `brandName`, etc. from env vars if you want them per-environment.
126
+
127
+ ---
128
+
129
+ ## 6. Hiding the widget on auth pages
130
+
131
+ Don't render `<WealthChat />` on `/login`, `/register`, or admin pages:
132
+
133
+ ```tsx
134
+ const AUTH_PATHS = ["/login", "/register"];
135
+ const isAuthPage = AUTH_PATHS.includes(pathname);
136
+ const isAdmin = pathname.startsWith("/admin");
137
+
138
+ {!isAuthPage && !isAdmin && <WealthChat ... />}
139
+ ```
140
+
141
+ ---
142
+
143
+ ## 7. Theming
144
+
145
+ Override the brand color via prop **or** with a CSS variable in your global styles:
146
+
147
+ ```css
148
+ .landing-theme {
149
+ --wac-brand: #1a2d5a; /* default */
150
+ --wac-brand-contrast: #ffffff;
151
+ --wac-bg: #ffffff;
152
+ --wac-fg: #1f2937;
153
+ --wac-muted: #6b7280;
154
+ --wac-border: #e5e7eb;
155
+ --wac-bot-bg: #f3f4f6;
156
+ --wac-user-bg: var(--wac-brand);
157
+ --wac-chip-bg: #eef2ff;
158
+ --wac-chip-fg: #1e293b;
159
+ --wac-radius: 14px;
160
+ --wac-shadow: 0 12px 40px rgba(0, 0, 0, 0.18);
161
+ }
162
+ ```
163
+
164
+ All chat colors are CSS-variable-driven so dark mode can be added by toggling a class on `<html>`.
165
+
166
+ ---
167
+
168
+ ## 8. Troubleshooting
169
+
170
+ | Symptom | Cause | Fix |
171
+ |---|---|---|
172
+ | 💬 button doesn't appear | Library not bundled, or `<WealthChat>` not mounted | Verify import compiles; check the layout actually renders it |
173
+ | Clicking 💬 shows AuthGate even when logged in | JWT not in `localStorage` under one of the auto-discovered keys (`token`, `access_token`, `auth_token`, `jwt`) | DevTools → Application → Local Storage → rename your key, or write `wac_session` directly (see §3) |
174
+ | `401 Unauthorized` on `/chat-widget/me` | JWT invalid / expired / wrong issuer | Re-login; verify your backend's `get_current_user` accepts the JWT format |
175
+ | `404 /chat-widget/chat/chip/...` (path doubled) | `apiBase` already includes `/chat-widget` AND old library version | Update to library ≥ 0.1.0 which uses `/chip/{id}` not `/chat/chip/{id}` |
176
+ | Stuck "compiling" on first chat open | Next.js dev cold-start | Wait ~5 s for first compile, subsequent loads are instant |
177
+ | Bold text shows as literal `*asterisks*` | Library not rebuilt after markdown-it added | Update to library ≥ 0.1.0 |
178
+ | Floating button + dark area visible behind chat panel | Page background bleeding through | Library widget is positioned correctly; check that no global CSS overrides `position: fixed` on the widget div |
179
+
180
+ For backend-side issues (chip not routing, wrong endpoint called, `telegram_id` missing) see **[BACKEND_CHAT_WIDGET.md](./BACKEND_CHAT_WIDGET.md)**.