@helpai/elements 1.8.0 → 1.10.0

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.
@@ -0,0 +1,523 @@
1
+ # Troubleshooting
2
+
3
+ The widget isn't working as expected. Let's diagnose.
4
+
5
+ ---
6
+
7
+ ## Quick diagnostic checklist
8
+
9
+ Paste this into your browser console on the page where the widget
10
+ should be:
11
+
12
+ ```js
13
+ // 1. Is the bundle loaded?
14
+ console.log('HelpAI:', typeof window.HelpAI);
15
+ // Expected: "function" (after bundle loads)
16
+
17
+ // 2. Is an instance mounted?
18
+ console.log('instance:', window.HelpAI?.getInstance?.());
19
+ // Expected: an object with open/close/expand methods, not null
20
+
21
+ // 3. Is the shadow host in the DOM?
22
+ console.log('host:', document.querySelector('helpai-chat-host, web-ai-chat'));
23
+ // Expected: a DOM element
24
+
25
+ // 4. What's the current data-mode?
26
+ const host = document.querySelector('helpai-chat-host, web-ai-chat');
27
+ console.log('data-mode:', host?.dataset.mode);
28
+ // Expected: "closed" / "open" / "expanded" / "fullscreen" / "inline" / "standalone"
29
+
30
+ // 5. What storage does the widget have?
31
+ for (const k of Object.keys(localStorage)) {
32
+ if (k.startsWith('helpai.')) console.log(k, '=', localStorage.getItem(k));
33
+ }
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Widget doesn't appear
39
+
40
+ ### Symptom: no bubble in the corner
41
+
42
+ 1. **Check the console for errors** — most commonly a network error
43
+ loading `cdn.help.ai/sdk/js/elements/latest/elements.js` (note the versioned
44
+ path — the bare `cdn.help.ai/elements.js` root 404s). Verify the URL in
45
+ DevTools → Network tab.
46
+
47
+ 2. **Is the deployment identity complete?**
48
+
49
+ ```js
50
+ // In console:
51
+ window.HelpAI?.getInstance?.();
52
+ ```
53
+
54
+ Returns `null` if no instance mounted. Likely causes:
55
+ - Missing required `data-ai-agent-deployment-id` on the install script.
56
+ - `init()` called without `aiAgentDeploymentId`. `publicKey` is optional only for intentional guest mode.
57
+ - Typo in the key or deployment ID.
58
+
59
+ 3. **Is the host element in the DOM?**
60
+
61
+ ```js
62
+ document.querySelector('helpai-chat-host');
63
+ ```
64
+
65
+ If null, the widget didn't mount. Check the console for init errors.
66
+
67
+ 4. **Is the host hidden by host-page CSS?** — shouldn't happen (the
68
+ widget uses `all: initial` to reset), but if you've got a very
69
+ aggressive `* { display: none !important; }` style somewhere:
70
+
71
+ ```js
72
+ const host = document.querySelector('helpai-chat-host');
73
+ console.log(getComputedStyle(host).display); // should be "block" or "inline-block"
74
+ ```
75
+
76
+ 5. **Is `launcher.hidden: true` set?** — that hides the FAB. Check
77
+ your options.
78
+
79
+ 6. **Is `mode` set to `inline` or `standalone`?** — those modes don't
80
+ show a launcher. Check your options.
81
+
82
+ ### Symptom: bubble shows but clicking does nothing
83
+
84
+ 1. **Check the network tab** — clicking fires `/pai/handshake`.
85
+ An opaque 404 means the credentials did not match an enabled deployment or
86
+ the requesting origin was not allowed. 0 (blocked) means a CORS / ad-blocker
87
+ issue.
88
+
89
+ 2. **Are `x-public-key` and `x-ai-agent-deployment-id` making it through?** —
90
+ some corporate proxies strip custom headers. Test on a personal network.
91
+
92
+ 3. **Any CSP errors in the console?** — `"Refused to connect to
93
+ https://help.ai/api/agent because it violates the following Content
94
+ Security Policy…"` means you need to add `help.ai` to your
95
+ `connect-src`. See [Security § CSP](./security-privacy.md#content-security-policy).
96
+
97
+ ---
98
+
99
+ ## Widget appears but doesn't behave right
100
+
101
+ ### Symptom: "Failed to fetch" errors on open
102
+
103
+ Network issue. Common causes:
104
+
105
+ - **Ad blocker** — uBlock Origin / AdBlock Plus sometimes block
106
+ `help.ai` API calls as tracking. Whitelist it.
107
+ - **Corporate proxy** — some proxies block `text/event-stream`
108
+ (streaming responses). Test on a different network.
109
+ - **CSP** — your Content Security Policy is missing
110
+ `help.ai` in `connect-src`.
111
+
112
+ ### Symptom: panel appears in the wrong spot on mobile
113
+
114
+ Only in `floating` mode. The widget uses `position: fixed` anchored
115
+ to `presentation.position` (`bottom-right` default). If mobile viewport
116
+ units are behaving oddly (iOS Safari, looking at you):
117
+
118
+ 1. Ensure you have a viewport meta tag:
119
+
120
+ ```html
121
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
122
+ ```
123
+
124
+ 2. Avoid having `overflow: hidden` on `<html>` or `<body>` — this
125
+ clips fixed positioning on some browsers.
126
+
127
+ 3. If still stuck, try `presentation.mode: "inline"` inside a dedicated container
128
+ on mobile breakpoints.
129
+
130
+ ### Symptom: panel doesn't fill its container (inline mode)
131
+
132
+ The parent container needs explicit dimensions. Inline widgets fill
133
+ 100% × 100% of their parent, which falls back to `min-height: 320px`
134
+ if the parent is 0-height.
135
+
136
+ ```css
137
+ /* Parent needs a size */
138
+ .my-chat-container {
139
+ height: 500px;
140
+ /* or */
141
+ display: flex;
142
+ flex-direction: column;
143
+ /* with widget set to flex: 1 */
144
+ }
145
+
146
+ .my-chat-container web-ai-chat {
147
+ flex: 1 1 0;
148
+ min-height: 0; /* critical for flex children */
149
+ }
150
+ ```
151
+
152
+ ### Symptom: two widgets on the same page overlap or conflict
153
+
154
+ Use distinct `widgetId` values:
155
+
156
+ ```ts
157
+ init({ widgetId: 'support', publicKey: SUPPORT_KEY, aiAgentDeploymentId: SUPPORT_DEPLOYMENT_ID });
158
+ init({ widgetId: 'sales', publicKey: SALES_KEY, aiAgentDeploymentId: SALES_DEPLOYMENT_ID });
159
+ ```
160
+
161
+ And position them in different corners:
162
+
163
+ ```ts
164
+ init({ widgetId: 'support', presentation: { position: 'bottom-right' } });
165
+ init({ widgetId: 'sales', presentation: { position: 'bottom-left' } });
166
+ ```
167
+
168
+ Without distinct widgetIds, the second `init()` **replaces** the
169
+ first.
170
+
171
+ ---
172
+
173
+ ## Styling issues
174
+
175
+ ### Symptom: widget ignores my CSS
176
+
177
+ That's by design. The widget lives inside a shadow root — your page
178
+ CSS can't reach inside. Use the [theming options](./theming.md)
179
+ instead.
180
+
181
+ For advanced customization, override CSS custom properties on the
182
+ host element:
183
+
184
+ ```css
185
+ web-ai-chat,
186
+ helpai-chat-host {
187
+ --helpai-accent: #ff3e00;
188
+ --helpai-radius: 16px;
189
+ --helpai-font: "Söhne", system-ui, sans-serif;
190
+ }
191
+ ```
192
+
193
+ These cascade through the shadow root.
194
+
195
+ ### Symptom: the widget inherits my weird base font
196
+
197
+ Most likely the host page uses `* { font-family: X !important }`.
198
+ The widget's `all: initial` reset handles most cases, but extreme
199
+ overrides can leak.
200
+
201
+ Force the widget's font explicitly via option:
202
+
203
+ ```ts
204
+ init({
205
+ theme: {
206
+ fontFamily: 'Inter, system-ui, sans-serif',
207
+ },
208
+ });
209
+ ```
210
+
211
+ ### Symptom: launcher bubble is a different color than expected
212
+
213
+ Check:
214
+
215
+ 1. `theme.accent` in your `init()` options.
216
+ 2. The workspace default accent in your Help AI dashboard.
217
+ 3. `prefers-color-scheme` — the accent might look different in dark
218
+ mode vs. light.
219
+
220
+ To debug:
221
+
222
+ ```js
223
+ const host = document.querySelector('helpai-chat-host');
224
+ console.log('data-theme:', host.dataset.theme); // auto / light / dark
225
+ console.log('computed accent:', getComputedStyle(host).getPropertyValue('--helpai-accent'));
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Persistence issues
231
+
232
+ ### Symptom: chat history doesn't persist across reloads
233
+
234
+ 1. **Is localStorage available?** Private/incognito modes may disable it.
235
+
236
+ ```js
237
+ try {
238
+ localStorage.setItem('_test', '1');
239
+ console.log('OK');
240
+ } catch (e) {
241
+ console.error('Blocked', e);
242
+ }
243
+ ```
244
+
245
+ 2. **Check the widget's storage keys:**
246
+
247
+ ```js
248
+ for (const k of Object.keys(localStorage)) {
249
+ if (k.startsWith('helpai.')) console.log(k);
250
+ }
251
+ ```
252
+
253
+ You should see at least `helpai.default.visitorId` and (after a
254
+ chat) `helpai.default.conversationId`.
255
+
256
+ 3. **Is the `widgetId` consistent across reloads?** If you pass a
257
+ dynamic `widgetId` (e.g., based on current time), each load gets
258
+ a fresh namespace and history won't carry over.
259
+
260
+ ### Symptom: user switches devices, chat history doesn't follow
261
+
262
+ That's the designed behavior for the current conversationId (per-device).
263
+ What DOES sync across devices:
264
+
265
+ - Locale preference.
266
+ - Theme mode.
267
+ - Sound mute toggle.
268
+ - Drag-resized panel dimensions.
269
+
270
+ These sync via the backend's `userPrefs` store, keyed by the widget's
271
+ own anonymous `visitorId`. Chat messages themselves are tied to that
272
+ `visitorId`, which is per-browser. `userContext` is informational
273
+ context for the AI only — it does not link chats across devices.
274
+
275
+ ---
276
+
277
+ ## React / framework issues
278
+
279
+ ### Symptom: "window is not defined" during build
280
+
281
+ You're calling `init()` at module scope or during SSR. Move it
282
+ inside a lifecycle hook that only fires in the browser:
283
+
284
+ ```tsx
285
+ // Wrong:
286
+ const chat = init({ publicKey, aiAgentDeploymentId }); // runs during SSR
287
+
288
+ export function App() { ... }
289
+
290
+ // Right:
291
+ export function App() {
292
+ useEffect(() => {
293
+ const chat = init({ publicKey, aiAgentDeploymentId });
294
+ return () => chat.destroy();
295
+ }, []);
296
+ }
297
+ ```
298
+
299
+ ### Symptom: widget mounts twice in React
300
+
301
+ React 18 strict mode intentionally double-invokes `useEffect` in
302
+ development. Your cleanup must be complete:
303
+
304
+ ```tsx
305
+ useEffect(() => {
306
+ const chat = init({ publicKey, aiAgentDeploymentId });
307
+ return () => chat.destroy(); // ← this MUST unmount cleanly
308
+ }, []);
309
+ ```
310
+
311
+ If you see "instance already exists at widgetId", your cleanup isn't
312
+ running. Check for missed `return () => …`.
313
+
314
+ ### Symptom: HMR leaves stale widgets on the page
315
+
316
+ During Vite / Next.js HMR, the module re-runs without the React tree
317
+ being torn down. Symptoms: multiple launchers appear, old styles
318
+ stick around.
319
+
320
+ Workaround: use `init()` (idempotent) instead of `mount()`. `init()`
321
+ auto-replaces any existing instance at the same `widgetId`.
322
+
323
+ ### Symptom: TypeScript complains about `<web-ai-chat>`
324
+
325
+ You're using the web component in React < 19. Add a type
326
+ declaration:
327
+
328
+ ```ts
329
+ // src/web-ai-chat.d.ts
330
+ declare namespace JSX {
331
+ interface IntrinsicElements {
332
+ 'web-ai-chat': React.DetailedHTMLProps<
333
+ React.HTMLAttributes<HTMLElement> & {
334
+ 'public-key'?: string;
335
+ mode?: 'floating' | 'inline' | 'standalone';
336
+ theme?: 'auto' | 'light' | 'dark';
337
+ },
338
+ HTMLElement
339
+ >;
340
+ }
341
+ }
342
+ ```
343
+
344
+ ---
345
+
346
+ ## Event / hook issues
347
+
348
+ ### Symptom: `.on('message', ...)` never fires
349
+
350
+ 1. **Is the instance really mounted?**
351
+
352
+ ```js
353
+ const chat = init({ publicKey: '...', aiAgentDeploymentId: '01900000-0000-7000-8000-000000000000' });
354
+ console.log(chat.widgetId); // should log the id
355
+ ```
356
+
357
+ 2. **Are messages being sent?** Open the Network tab, filter
358
+ `stream-message`. A send should produce a POST.
359
+
360
+ 3. **Is the listener attached BEFORE you send?** If you attach after
361
+ the user sent a message, you miss that one.
362
+
363
+ ### Symptom: pre-boot hooks never fire
364
+
365
+ 1. **Is the bundle loading?** Check Network → `elements.js` →
366
+ status 200.
367
+
368
+ 2. **Is `window.HelpAIHooks` an array?**
369
+
370
+ ```js
371
+ Array.isArray(window.HelpAIHooks); // should be true
372
+ ```
373
+
374
+ 3. **Is the entry shape correct?**
375
+ ```js
376
+ // Tuple form:
377
+ window.HelpAIHooks.push(['*', '*', (inst, e, d) => console.log(e)]);
378
+ // Object form:
379
+ window.HelpAIHooks.push({ widgetId: '*', event: '*', listener: fn });
380
+ ```
381
+
382
+ ---
383
+
384
+ ## Performance issues
385
+
386
+ ### Symptom: page feels slow when widget loads
387
+
388
+ 1. **Are you using `defer` on the script tag?** Without `defer`, the
389
+ script blocks HTML parsing.
390
+
391
+ 2. **Check the Network tab** — the bundle should be ~107 KB gzipped (budget 108 KB)
392
+ and load in well under a second on a good connection.
393
+
394
+ 3. **Are you self-hosting an older version?** Pull the latest.
395
+ Older versions were larger.
396
+
397
+ 4. **Lighthouse or WebPageTest** — run a test. The widget is
398
+ deferred and shouldn't affect LCP. If you see CLS spikes, the
399
+ widget's host element might be affecting layout — ensure
400
+ `position: fixed` is working (floating mode) or the inline
401
+ container has a stable size.
402
+
403
+ ### Symptom: streaming feels choppy
404
+
405
+ 1. **Slow backend** — the widget just renders what it receives. Check
406
+ the stream latency in Network → the `stream-message` entry.
407
+
408
+ 2. **Network conditions** — SSE is sensitive to packet loss. Try a
409
+ different network.
410
+
411
+ 3. **Buffered response mode** — try `behavior.responseMode: "buffered"` if
412
+ streaming is unusable on your users' networks. Tradeoff: they wait
413
+ for the whole reply before seeing anything.
414
+
415
+ ---
416
+
417
+ ## CORS / CSP / networking
418
+
419
+ ### Symptom: CORS errors in the console
420
+
421
+ Our API is CORS-permissive to every origin. If you're seeing CORS
422
+ errors:
423
+
424
+ - **Check the URL** — `https://help.ai` (the bare origin — not a
425
+ relative path; the widget appends `/api/agent` + `/api/data` itself).
426
+ - **Check for browser extensions** blocking `help.ai`.
427
+ - **Check your reverse proxy** — some enterprise proxies strip CORS
428
+ headers.
429
+
430
+ ### Symptom: CSP blocks the widget
431
+
432
+ Add to your Content Security Policy:
433
+
434
+ ```
435
+ script-src 'self' https://cdn.help.ai;
436
+ connect-src 'self' https://help.ai;
437
+ style-src 'self' 'unsafe-inline'; # Shadow DOM inline styles
438
+ img-src 'self' data: blob: https:;
439
+ font-src 'self' https:;
440
+ media-src 'self' blob:; # For voice recording
441
+ ```
442
+
443
+ Full CSP guide: [Security § CSP](./security-privacy.md#content-security-policy).
444
+
445
+ ---
446
+
447
+ ## Widget-specific behavior questions
448
+
449
+ ### "The widget opens to a stale chat thread — how do I reset?"
450
+
451
+ Click the trash icon (Clear action) in the panel → "Start new
452
+ conversation". This creates a fresh conversationId.
453
+
454
+ Programmatic:
455
+
456
+ ```ts
457
+ // Clear the persisted conversationId + unmount, then remount
458
+ for (const k of Object.keys(localStorage)) {
459
+ if (k.startsWith('helpai.') && k.endsWith('.conversationId')) localStorage.removeItem(k);
460
+ }
461
+ destroy();
462
+ init({ publicKey: 'agent_pk_...', aiAgentDeploymentId: '01900000-0000-7000-8000-000000000000' });
463
+ ```
464
+
465
+ ### "Can I send a message programmatically for the user?"
466
+
467
+ Not via the public API — by design. Messages must come from user
468
+ input (so they see + confirm what's being sent). Use **welcome
469
+ suggestions** in your dashboard for "prefilled" conversation
470
+ starters.
471
+
472
+ ### "Can I get the full chat transcript?"
473
+
474
+ From the client, no direct API. Subscribe to `message` events and
475
+ build your own log:
476
+
477
+ ```ts
478
+ const transcript = [];
479
+ chat.on('message', (m) => transcript.push(m));
480
+ ```
481
+
482
+ Via the backend, yes — query the chats API with your visitor's
483
+ conversation token. Contact support for the server-side endpoint docs.
484
+
485
+ ### "Why can't I see what the user is typing in real-time?"
486
+
487
+ Privacy. The widget doesn't emit events for in-progress typing —
488
+ only committed messages (Enter / Send click). If you need
489
+ keystroke-level telemetry, that's not supported.
490
+
491
+ ### "The widget's language is wrong for my user"
492
+
493
+ Resolution precedence:
494
+
495
+ 1. User's persisted pick (language switcher).
496
+ 2. `init({ i18n: { locale: '...' } })`.
497
+ 3. `init({ i18n: { defaultLocale: '...' } })`.
498
+ 4. `navigator.language` (when `i18n.defaultLocale: "auto"`).
499
+ 5. English (`"en"`).
500
+
501
+ To force: `init({ i18n: { locale: 'fr-CA' } })`. To let the visitor switch,
502
+ provide `i18n.availableLocales` and include `language` in `header.actions`.
503
+
504
+ ---
505
+
506
+ ## Getting more help
507
+
508
+ 1. **Read the [FAQ](./faq.md)** — most common questions answered
509
+ there.
510
+
511
+ 2. **Check our GitHub issues**: [github.com/help-ai/elements](https://github.com/help-ai/elements/issues).
512
+
513
+ 3. **Email support**: [support@help.ai](mailto:support@help.ai). Include:
514
+ - The URL of the page where the issue appears.
515
+ - Browser + version.
516
+ - Screenshot + console output.
517
+ - Steps to reproduce.
518
+
519
+ 4. **Status page**: [help.ai/status](https://help.ai/status) — check
520
+ for ongoing incidents before troubleshooting.
521
+
522
+ 5. **Debug mode**: add `?helpai-debug=1` to your URL. The widget
523
+ logs its internal state to the console (prefixed with `[helpai]`).