@helpai/elements 1.8.0 → 1.9.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.
- package/README.md +34 -12
- package/configurator.mjs +1 -1
- package/docs/README.md +183 -0
- package/docs/analytics.md +551 -0
- package/docs/api-reference.md +622 -0
- package/docs/browser-support.md +302 -0
- package/docs/configuration.md +1001 -0
- package/docs/events-and-hooks.md +483 -0
- package/docs/faq.md +435 -0
- package/docs/frameworks.md +640 -0
- package/docs/installation.md +626 -0
- package/docs/options.md +323 -0
- package/docs/quickstart.md +229 -0
- package/docs/recipes.md +725 -0
- package/docs/security-privacy.md +575 -0
- package/docs/theming.md +570 -0
- package/docs/troubleshooting.md +523 -0
- package/elements-web-component.esm.js +39 -39
- package/elements-web-component.esm.js.map +4 -4
- package/elements.cjs.js +39 -39
- package/elements.cjs.js.map +4 -4
- package/elements.esm.js +39 -39
- package/elements.esm.js.map +4 -4
- package/elements.js +39 -39
- package/elements.js.map +4 -4
- package/index.d.ts +84 -3
- package/index.mjs +13 -13
- package/jsx-react.d.ts +15 -0
- package/jsx-svelte.d.ts +14 -0
- package/jsx-vue.d.ts +14 -0
- package/llms-full.txt +7862 -0
- package/llms.txt +36 -0
- package/loader.js +3 -3
- package/loader.min.js +1 -1
- package/package.json +36 -1
- package/react.d.ts +3552 -0
- package/react.mjs +62 -0
- package/schema.d.ts +56 -0
- package/schema.json +24 -2
- package/schema.mjs +1 -1
- package/web-component.d.ts +1561 -17
- package/web-component.mjs +14 -14
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
Deep-dive for every install path. See [Quickstart](./quickstart.md)
|
|
4
|
+
for the 5-minute version.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Choose your path
|
|
9
|
+
|
|
10
|
+
| Path | Best for | Delivery |
|
|
11
|
+
| ----------------------------------------------------------------- | --------------------------------------------------- | ---------- |
|
|
12
|
+
| [A. `<script data-*>` auto-boot](#path-a--script-data--auto-boot) | Static HTML, WordPress, no build step | CDN |
|
|
13
|
+
| [B. Async loader snippet](#path-b--async-loader-snippet) | Tag managers, analytics pipelines | CDN |
|
|
14
|
+
| [C. NPM package](#path-c--npm-package) | React, Next.js, Vue, Svelte, SolidJS, any bundler | NPM |
|
|
15
|
+
| [React kit](./frameworks.md#react) | React, Next.js, Remix: `<HelpAI />` + `useHelpAI()` | NPM |
|
|
16
|
+
| [D. Web Component](#path-d--web-component) | Declarative tags in any framework | CDN or NPM |
|
|
17
|
+
|
|
18
|
+
> ### CDN URL
|
|
19
|
+
>
|
|
20
|
+
> The bundle is served from a **versioned** path — the bare root 404s:
|
|
21
|
+
>
|
|
22
|
+
> ```
|
|
23
|
+
> https://cdn.help.ai/sdk/js/elements/<version>/<file>
|
|
24
|
+
> ```
|
|
25
|
+
>
|
|
26
|
+
> - `…/elements/<version>/elements.js` — **pinned** to an exact release (immutable, recommended for production).
|
|
27
|
+
> - `…/elements/latest/elements.js` — always the newest release (a 302 to its pinned path, cached up to 4 hours).
|
|
28
|
+
>
|
|
29
|
+
> Files: `elements.js` (auto-boot + queue + tag), `elements-web-component.esm.js`
|
|
30
|
+
> (standalone web-component ESM). The examples below use `latest`; **pin a version
|
|
31
|
+
> in production** so a release never changes behaviour under you.
|
|
32
|
+
|
|
33
|
+
All four paths:
|
|
34
|
+
|
|
35
|
+
- Load the same JS bundle.
|
|
36
|
+
- Accept the same [configuration options](./configuration.md).
|
|
37
|
+
- Emit the same [events](./events-and-hooks.md).
|
|
38
|
+
- Respect the same [isolation guarantees](./security-privacy.md#shadow-dom--iframe).
|
|
39
|
+
|
|
40
|
+
You can combine them — e.g., a Next.js page can use path C, and a
|
|
41
|
+
marketing landing can use path A, both pointing at the same deployment.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Path A — `<script data-*>` auto-boot
|
|
46
|
+
|
|
47
|
+
Fastest path. Zero custom JavaScript.
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<!-- In your HTML <head> -->
|
|
51
|
+
<script src="https://cdn.help.ai/sdk/js/elements/latest/elements.js" data-public-key="agent_pk_abc123..." data-ai-agent-deployment-id="01900000-0000-7000-8000-000000000000" defer></script>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Attributes
|
|
55
|
+
|
|
56
|
+
The zero-JavaScript path intentionally exposes a small, flat subset of
|
|
57
|
+
`ChatWidgetOptions`. These are the attributes parsed by the current bundle:
|
|
58
|
+
|
|
59
|
+
| Attribute | Maps to | Example |
|
|
60
|
+
| ---------------------------------------------- | --------------------------------- | ------------------------------- |
|
|
61
|
+
| `data-public-key` | `publicKey` | `agent_pk_...` |
|
|
62
|
+
| `data-ai-agent-deployment-id` | `aiAgentDeploymentId` | deployment UUID |
|
|
63
|
+
| `data-base-url` | `baseUrl` | `https://help.ai` |
|
|
64
|
+
| `data-agent-api-base-url` | `agentApiBaseUrl` | `http://localhost:3069` |
|
|
65
|
+
| `data-api-base-url` | `dataApiBaseUrl` | `http://localhost:3071` |
|
|
66
|
+
| `data-theme` | `theme` | `auto` / `light` / `dark` |
|
|
67
|
+
| `data-mode` / `data-position` | `presentation` | `floating` / `bottom-right` |
|
|
68
|
+
| `data-iframe` | `presentation.iframe` | `true` |
|
|
69
|
+
| `data-width` / `data-height` | `presentation.size` | `420px` / `640px` |
|
|
70
|
+
| `data-response-mode` | `behavior.responseMode` | `streaming` / `buffered` |
|
|
71
|
+
| `data-start-minimized` | `behavior.startMinimized` | `false` |
|
|
72
|
+
| `data-show-reasoning` | `behavior.showReasoning` | `true` |
|
|
73
|
+
| `data-show-tool-calls` | `behavior.showToolCalls` | `true` |
|
|
74
|
+
| `data-show-sources` | `behavior.showSources` | `true` |
|
|
75
|
+
| `data-disable-message-feedback` | `behavior.disableMessageFeedback` | `true` |
|
|
76
|
+
| `data-auto-send-suggestions` | `behavior.autoSendSuggestions` | `true` |
|
|
77
|
+
| `data-pop-out-url` | `behavior.popOutUrl` | `https://chat.example.com` |
|
|
78
|
+
| `data-locale` | `i18n.locale` | `fr-FR` |
|
|
79
|
+
| `data-launcher-variant` / `-size` / `-icon` | `launcher` fields | `pill` / `lg` / `spark` |
|
|
80
|
+
| `data-launcher-label` / `-hidden` | `launcher` fields | `Ask AI` / `true` |
|
|
81
|
+
| `data-callout-text` / `-delay` | `launcher.callout` | `Hi! 👋` / `2000` |
|
|
82
|
+
| `data-actions` | `header.actions` (CSV) | `theme,history,close` |
|
|
83
|
+
| `data-features-disable-file-upload` | `features.disableFileUpload` | `true` |
|
|
84
|
+
| `data-features-voice` | `features.voice` | `local` / `server` / `disabled` |
|
|
85
|
+
| `data-endpoint-upload` / `-transcribe` | `endpoints` | `/pai/upload` |
|
|
86
|
+
| `data-user-id` / `-email` / `-name` | `userContext` scalar fields | `user_42` |
|
|
87
|
+
| `data-user-context` | `userContext` extras (JSON) | `'{"plan":"pro"}'` |
|
|
88
|
+
| `data-page-area` / `-url` / `-path` / `-title` | `pageContext` scalar fields | `support` |
|
|
89
|
+
| `data-page-context` | `pageContext` extras (JSON) | `'{"experiment":"b"}'` |
|
|
90
|
+
|
|
91
|
+
Unknown attributes are ignored. Full option semantics:
|
|
92
|
+
[Configuration](./configuration.md).
|
|
93
|
+
|
|
94
|
+
> Explicit scalar attributes (`data-user-id`, `data-page-area`, …) win
|
|
95
|
+
> over keys in the `data-user-context` / `data-page-context` JSON. For
|
|
96
|
+
> SPAs where the visitor logs in after load or navigates between pages,
|
|
97
|
+
> set context dynamically with
|
|
98
|
+
> [`instance.setUserContext(...)` / `setPageContext(...)`](./configuration.md#setting-context-after-login--on-navigation).
|
|
99
|
+
> The `data-user-*` / `data-page-*` attributes are reactive — updating
|
|
100
|
+
> one re-handshakes with the same anonymous visitor/conversation. This
|
|
101
|
+
> context is informational for the AI, never an auth credential.
|
|
102
|
+
|
|
103
|
+
### `data-no-autoboot`
|
|
104
|
+
|
|
105
|
+
Add this flag to load the bundle **without** automatically mounting.
|
|
106
|
+
Useful when you want to `init()` later from JavaScript:
|
|
107
|
+
|
|
108
|
+
```html
|
|
109
|
+
<script src="https://cdn.help.ai/sdk/js/elements/latest/elements.js" data-no-autoboot></script>
|
|
110
|
+
<script>
|
|
111
|
+
// Mount later, conditionally
|
|
112
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
113
|
+
if (shouldShowChat()) {
|
|
114
|
+
HelpAI.init({ publicKey: 'agent_pk_...', aiAgentDeploymentId: '01900000-0000-7000-8000-000000000000' });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
</script>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Multi-widget
|
|
121
|
+
|
|
122
|
+
For two widgets on one page, load the bundle once and call `mount()` with
|
|
123
|
+
distinct `widgetId` values:
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
HelpAI.mount({ widgetId: 'support', publicKey: SUPPORT_KEY, aiAgentDeploymentId: SUPPORT_DEPLOYMENT_ID });
|
|
127
|
+
HelpAI.mount({ widgetId: 'sales', publicKey: SALES_KEY, aiAgentDeploymentId: SALES_DEPLOYMENT_ID });
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Path B — async loader snippet
|
|
133
|
+
|
|
134
|
+
Tag-manager-style snippet. Defers the bundle download without blocking
|
|
135
|
+
first paint, while letting your page queue commands immediately.
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
<script>
|
|
139
|
+
(function (w, d) {
|
|
140
|
+
w.HelpAI =
|
|
141
|
+
w.HelpAI ||
|
|
142
|
+
function () {
|
|
143
|
+
(w.HelpAI.q = w.HelpAI.q || []).push(arguments);
|
|
144
|
+
};
|
|
145
|
+
w.HelpAIHooks = w.HelpAIHooks || [];
|
|
146
|
+
var s = d.createElement('script');
|
|
147
|
+
s.async = 1;
|
|
148
|
+
s.src = 'https://cdn.help.ai/sdk/js/elements/latest/elements.js';
|
|
149
|
+
d.head.appendChild(s);
|
|
150
|
+
})(window, document);
|
|
151
|
+
|
|
152
|
+
// Queue commands — they run in order once the bundle arrives.
|
|
153
|
+
HelpAI('init', { publicKey: 'agent_pk_abc123...', aiAgentDeploymentId: '01900000-0000-7000-8000-000000000000' });
|
|
154
|
+
</script>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Queueable commands
|
|
158
|
+
|
|
159
|
+
| Call | What it does |
|
|
160
|
+
| ----------------------------------------- | ---------------------------------------------------------------- |
|
|
161
|
+
| `HelpAI('init', opts)` | Mount once; update the same widget id |
|
|
162
|
+
| `HelpAI('mount', opts)` | Mount fresh; replace the same widget id |
|
|
163
|
+
| `HelpAI('destroy')` | Unmount all live widgets |
|
|
164
|
+
| `HelpAI('open')` | Open the default widget's panel |
|
|
165
|
+
| `HelpAI('close')` | Close the default widget's panel |
|
|
166
|
+
| `HelpAI('expand')` | Toggle the default widget's expanded tier |
|
|
167
|
+
| `HelpAI('ask', text, { send })` | Open the chat with `text` in the composer; `send: true` sends it |
|
|
168
|
+
| `HelpAI('on', eventName, fn)` | Subscribe to a default-widget event |
|
|
169
|
+
| `HelpAI('update', { /* patch */ })` | Update the default widget's options |
|
|
170
|
+
| `HelpAI('hook', widgetId, eventName, fn)` | Register a global lifecycle hook |
|
|
171
|
+
|
|
172
|
+
### Pre-boot hooks
|
|
173
|
+
|
|
174
|
+
`window.HelpAIHooks` is a pre-boot queue for event listeners. Push
|
|
175
|
+
arrays or objects to it before the bundle arrives:
|
|
176
|
+
|
|
177
|
+
```html
|
|
178
|
+
<script>
|
|
179
|
+
window.HelpAIHooks = window.HelpAIHooks || [];
|
|
180
|
+
|
|
181
|
+
// Fire for every event on every widget
|
|
182
|
+
window.HelpAIHooks.push([
|
|
183
|
+
'*',
|
|
184
|
+
'*',
|
|
185
|
+
(inst, event, data) => {
|
|
186
|
+
console.log('chat event:', event, 'from', inst.widgetId, data);
|
|
187
|
+
},
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
// Or target one widget + one event
|
|
191
|
+
window.HelpAIHooks.push([
|
|
192
|
+
'support',
|
|
193
|
+
'message',
|
|
194
|
+
(inst, _event, msg) => {
|
|
195
|
+
analytics.track('chat_message', msg);
|
|
196
|
+
},
|
|
197
|
+
]);
|
|
198
|
+
</script>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Full details: [Events & hooks](./events-and-hooks.md#global-lifecycle-hooks).
|
|
202
|
+
|
|
203
|
+
### After boot
|
|
204
|
+
|
|
205
|
+
Once the bundle loads, `window.HelpAI` becomes a real API:
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
window.HelpAI.init(opts)
|
|
209
|
+
window.HelpAI.destroy(widgetId?)
|
|
210
|
+
window.HelpAI.getInstance(widgetId?) // returns handle or null
|
|
211
|
+
window.HelpAI.getAllInstances()
|
|
212
|
+
window.HelpAI.hook(widgetId, event, fn)
|
|
213
|
+
window.HelpAI.open() / close() / expand()
|
|
214
|
+
window.HelpAI.version // semver string
|
|
215
|
+
window.HelpAI.brand // { id, displayName, cdnUrl, ... }
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Path C — NPM package
|
|
221
|
+
|
|
222
|
+
For sites with a bundler. Full framework-specific walkthroughs:
|
|
223
|
+
[Framework integrations](./frameworks.md).
|
|
224
|
+
|
|
225
|
+
### Install
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
npm install @helpai/elements
|
|
229
|
+
# or
|
|
230
|
+
yarn add @helpai/elements
|
|
231
|
+
# or
|
|
232
|
+
pnpm add @helpai/elements
|
|
233
|
+
# or
|
|
234
|
+
bun add @helpai/elements
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Entry points
|
|
238
|
+
|
|
239
|
+
The package ships two entries:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
// Main entry — everything except the web component.
|
|
243
|
+
import { init, mount, destroy, hook } from '@helpai/elements';
|
|
244
|
+
// Sub-entry — registers <web-ai-chat> as a custom element.
|
|
245
|
+
import '@helpai/elements/web-component';
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Tree-shaking: the main entry has zero mount-on-import side effects.
|
|
249
|
+
Unused exports are dropped by your bundler. Importing only types costs
|
|
250
|
+
zero runtime bytes.
|
|
251
|
+
|
|
252
|
+
### Minimal React usage
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
import { init, type ChatWidgetInstance } from '@helpai/elements';
|
|
256
|
+
import { useEffect, useRef } from 'react';
|
|
257
|
+
|
|
258
|
+
export function Chat() {
|
|
259
|
+
const instance = useRef<ChatWidgetInstance | null>(null);
|
|
260
|
+
|
|
261
|
+
useEffect(() => {
|
|
262
|
+
instance.current = init({
|
|
263
|
+
publicKey: import.meta.env.VITE_HELPAI_KEY,
|
|
264
|
+
aiAgentDeploymentId: import.meta.env.VITE_HELPAI_DEPLOYMENT_ID,
|
|
265
|
+
});
|
|
266
|
+
return () => instance.current?.destroy();
|
|
267
|
+
}, []);
|
|
268
|
+
|
|
269
|
+
return null; // the widget renders its own DOM outside your tree
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Typed options
|
|
274
|
+
|
|
275
|
+
Autocomplete for every option:
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
import type { ChatWidgetOptions } from '@helpai/elements';
|
|
279
|
+
|
|
280
|
+
const opts: ChatWidgetOptions = {
|
|
281
|
+
publicKey: 'agent_pk_...',
|
|
282
|
+
aiAgentDeploymentId: '01900000-0000-7000-8000-000000000000',
|
|
283
|
+
presentation: { mode: 'floating' },
|
|
284
|
+
theme: { accent: '#ff3e00' },
|
|
285
|
+
launcher: { variant: 'pill', label: 'Ask AI' },
|
|
286
|
+
// ...
|
|
287
|
+
};
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### SSR
|
|
291
|
+
|
|
292
|
+
The widget is client-only. Never call `init()` on the server. In
|
|
293
|
+
Next.js app router, wrap your component with `'use client'` (shown
|
|
294
|
+
above). In Remix / SvelteKit, use the `onMount` / `useEffect`
|
|
295
|
+
equivalent to defer init until the browser.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Path D — Web Component
|
|
300
|
+
|
|
301
|
+
Declarative tag. Works in any framework or plain HTML.
|
|
302
|
+
|
|
303
|
+
### Load + use
|
|
304
|
+
|
|
305
|
+
```html
|
|
306
|
+
<!-- Load the bundle once (anywhere in the page) -->
|
|
307
|
+
<script src="https://cdn.help.ai/sdk/js/elements/latest/elements.js" defer></script>
|
|
308
|
+
|
|
309
|
+
<!-- Then use the element anywhere, any number of times -->
|
|
310
|
+
<web-ai-chat public-key="agent_pk_..." ai-agent-deployment-id="..." mode="inline" theme="auto" style="display: block; height: 480px"></web-ai-chat>
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Attributes
|
|
314
|
+
|
|
315
|
+
Same parser fields as `<script data-*>`, but without HTML's outer
|
|
316
|
+
`data-` dataset prefix. Values are strings (booleans are `"true"` /
|
|
317
|
+
`"false"`). `data-api-base-url` keeps its name because `data` refers to
|
|
318
|
+
the data API.
|
|
319
|
+
|
|
320
|
+
Reactive attributes re-resolve the live widget. `iframe` and
|
|
321
|
+
`start-minimized` are mount-time-only.
|
|
322
|
+
|
|
323
|
+
```js
|
|
324
|
+
document.querySelector('web-ai-chat').setAttribute('theme', 'dark');
|
|
325
|
+
// The widget flips to dark mode.
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
The element also has the panel methods: `open()`, `close()`, `expand()`, `openForm(id)` and `ask(text, options)`,
|
|
329
|
+
which opens the chat with `text` in the composer for the visitor to send (`{ send: true }` sends it):
|
|
330
|
+
|
|
331
|
+
```js
|
|
332
|
+
document.querySelector('web-ai-chat').ask('How do I book a session?');
|
|
333
|
+
document.querySelector('web-ai-chat').ask('How do I book a session?', { send: true });
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Default mode
|
|
337
|
+
|
|
338
|
+
A bare `<web-ai-chat>` tag initially resolves to `mode="floating"`;
|
|
339
|
+
the handshake's `config.presentation.mode` supplies the deployment mode
|
|
340
|
+
when the host did not set one.
|
|
341
|
+
|
|
342
|
+
### Container sizing (`inline`, `page`, `standalone`)
|
|
343
|
+
|
|
344
|
+
These modes have the host element declare `width: 100%; height: 100%`
|
|
345
|
+
inside its shadow root. Width fills the parent automatically; **height
|
|
346
|
+
only resolves if the parent has a defined height**. An undefined parent
|
|
347
|
+
height collapses the widget to `0px` tall and it appears empty.
|
|
348
|
+
|
|
349
|
+
```html
|
|
350
|
+
<!-- ✓ Viewport-filling page -->
|
|
351
|
+
<html style="height: 100%">
|
|
352
|
+
<body style="height: 100%; margin: 0">
|
|
353
|
+
<web-ai-chat mode="page" public-key="…" ai-agent-deployment-id="…"></web-ai-chat>
|
|
354
|
+
</body>
|
|
355
|
+
</html>
|
|
356
|
+
|
|
357
|
+
<!-- ✓ Sized container (any of px / vh / 100% chain) -->
|
|
358
|
+
<div style="height: 600px">
|
|
359
|
+
<web-ai-chat mode="page" public-key="…" ai-agent-deployment-id="…"></web-ai-chat>
|
|
360
|
+
</div>
|
|
361
|
+
|
|
362
|
+
<!-- ✓ Flex / grid child with explicit sizing -->
|
|
363
|
+
<body style="display: flex; flex-direction: column; height: 100vh; margin: 0">
|
|
364
|
+
<header>…</header>
|
|
365
|
+
<web-ai-chat mode="page" style="flex: 1 1 0; min-height: 0" …></web-ai-chat>
|
|
366
|
+
</body>
|
|
367
|
+
|
|
368
|
+
<!-- ✗ Body has no defined height → host's 100% resolves to 0 -->
|
|
369
|
+
<body>
|
|
370
|
+
<web-ai-chat mode="page" …></web-ai-chat>
|
|
371
|
+
</body>
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
`floating` mode is unaffected — the host is `position: fixed` and
|
|
375
|
+
the panel's size is set by `panelSize` (`normal` / `expanded` /
|
|
376
|
+
`fullscreen`), independent of the parent. `modal` and `drawer` are
|
|
377
|
+
also self-sized: modal anchors to the viewport center; drawer docks
|
|
378
|
+
to the side. See `../features/modes.md` §
|
|
379
|
+
4.4 for the full sizing spec.
|
|
380
|
+
|
|
381
|
+
### Inside a framework
|
|
382
|
+
|
|
383
|
+
Most frameworks treat custom elements like any other HTML element:
|
|
384
|
+
|
|
385
|
+
**React 19+** — native support:
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
<web-ai-chat public-key='agent_pk_...' ai-agent-deployment-id='01900000-0000-7000-8000-000000000000' mode='inline' />
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**React < 19** — the element works but TypeScript doesn't know the
|
|
392
|
+
custom type. Declare it:
|
|
393
|
+
|
|
394
|
+
```ts
|
|
395
|
+
declare module 'react' {
|
|
396
|
+
namespace JSX {
|
|
397
|
+
interface IntrinsicElements {
|
|
398
|
+
'web-ai-chat': {
|
|
399
|
+
'public-key'?: string;
|
|
400
|
+
mode?: 'floating' | 'inline' | 'standalone' | 'page' | 'modal' | 'drawer';
|
|
401
|
+
theme?: 'auto' | 'light' | 'dark';
|
|
402
|
+
// ... (full list in configuration.md)
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**Vue 3** — works out of the box.
|
|
410
|
+
|
|
411
|
+
**Svelte** — works with `<svelte:options>`-less components.
|
|
412
|
+
|
|
413
|
+
**Angular** — add `CUSTOM_ELEMENTS_SCHEMA` to your module.
|
|
414
|
+
|
|
415
|
+
Framework-by-framework details: [Framework integrations](./frameworks.md).
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Version pinning
|
|
420
|
+
|
|
421
|
+
The bundle URL `https://cdn.help.ai/sdk/js/elements/latest/elements.js` always serves the
|
|
422
|
+
latest stable version. For reproducibility, pin to a specific
|
|
423
|
+
version:
|
|
424
|
+
|
|
425
|
+
```html
|
|
426
|
+
<script src="https://cdn.help.ai/v1/elements.js" defer></script>
|
|
427
|
+
<!-- or -->
|
|
428
|
+
<script src="https://cdn.help.ai/v1.2.0/elements.js" defer></script>
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Pinning means you control upgrade timing. The floating URL means you
|
|
432
|
+
get new features + bug fixes automatically. Both are supported; most
|
|
433
|
+
customers use the floating URL for marketing sites and pinned
|
|
434
|
+
versions for mission-critical apps.
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Path E — Standalone ESM bundle (no build, no NPM install)
|
|
439
|
+
|
|
440
|
+
For pages without a build step (plain HTML, MDX, sandbox demos, code
|
|
441
|
+
playgrounds, AI-generated UIs), load the pre-bundled ESM file shipped
|
|
442
|
+
with every release. `elements.esm.js` is fully self-contained — every
|
|
443
|
+
dependency (preact, signals, streaming-markdown) is inlined. No bare
|
|
444
|
+
specifiers, no import maps, no `+esm` magic. Drop into any page with
|
|
445
|
+
`<script type="module">`.
|
|
446
|
+
|
|
447
|
+
### Programmatic mount
|
|
448
|
+
|
|
449
|
+
```html
|
|
450
|
+
<script type="module">
|
|
451
|
+
// Pin an exact version for production (see Caching & versioning below).
|
|
452
|
+
import { mount } from 'https://cdn.jsdelivr.net/npm/@helpai/elements@<version>/elements.esm.js';
|
|
453
|
+
|
|
454
|
+
mount({
|
|
455
|
+
publicKey: 'agent_pk_…',
|
|
456
|
+
aiAgentDeploymentId: '…',
|
|
457
|
+
presentation: { mode: 'inline' },
|
|
458
|
+
target: document.querySelector('#chat'),
|
|
459
|
+
});
|
|
460
|
+
</script>
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Web component (declarative tag)
|
|
464
|
+
|
|
465
|
+
The web-component bundle self-registers `<web-ai-chat>` on import:
|
|
466
|
+
|
|
467
|
+
```html
|
|
468
|
+
<script type="module">
|
|
469
|
+
import 'https://cdn.jsdelivr.net/npm/@helpai/elements@<version>/elements-web-component.esm.js';
|
|
470
|
+
</script>
|
|
471
|
+
|
|
472
|
+
<web-ai-chat public-key="agent_pk_…" ai-agent-deployment-id="…" mode="inline"></web-ai-chat>
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### File catalog (every `dist/<brand>/` ships these)
|
|
476
|
+
|
|
477
|
+
| File | Purpose | Use with |
|
|
478
|
+
| --------------------------------- | ------------------------------------------------------ | --------------------------------------- |
|
|
479
|
+
| `elements.js` | IIFE bundle, defines `window.HelpAI` | Plain `<script>` (no `type="module"`) |
|
|
480
|
+
| `elements.esm.js` | **Standalone ESM, fully bundled** | `<script type="module">` import |
|
|
481
|
+
| `elements-web-component.esm.js` | **Standalone ESM, registers `<web-ai-chat>`** | `<script type="module">` import (no JS) |
|
|
482
|
+
| `index.mjs` / `web-component.mjs` | tsdown ESM with bare imports (`import "preact"` etc.) | Bundlers (Vite, webpack, esbuild) |
|
|
483
|
+
| `react.mjs` / `react.d.ts` | React kit: `<HelpAI />`, `useHelpAI()`, widget bundled | React apps, `@helpai/elements/react` |
|
|
484
|
+
| `style.css` | Standalone stylesheet — host-element tokens + layout | `<link rel="stylesheet">` |
|
|
485
|
+
| `loader.js`, `loader.min.js` | GA-style queueing loader stub | Tag managers / analytics pipelines |
|
|
486
|
+
| `index.d.ts` | TypeScript declarations | NPM consumers |
|
|
487
|
+
|
|
488
|
+
### CDN URL forms
|
|
489
|
+
|
|
490
|
+
| URL | Resolves to |
|
|
491
|
+
| --------------------------------------------------------------------------------- | -------------------------------------------------- |
|
|
492
|
+
| `https://cdn.help.ai/sdk/js/elements/latest/elements.js` | IIFE on the brand CDN |
|
|
493
|
+
| `https://cdn.help.ai/sdk/js/elements/latest/elements.esm.js` | Standalone ESM on the brand CDN |
|
|
494
|
+
| `https://cdn.jsdelivr.net/npm/@helpai/elements@<v>/elements.esm.js` | Standalone ESM on jsDelivr (immutable per version) |
|
|
495
|
+
| `https://cdn.jsdelivr.net/npm/@helpai/elements@<v>/elements-web-component.esm.js` | Web-component ESM on jsDelivr |
|
|
496
|
+
|
|
497
|
+
### Version pinning
|
|
498
|
+
|
|
499
|
+
| Pattern | Behavior |
|
|
500
|
+
| ------------ | ------------------------------------------------- |
|
|
501
|
+
| `@1.4.0` | Exact version. Immutable, max-age cached forever. |
|
|
502
|
+
| `@1`, `@1.4` | Semver-range — latest matching `1.x.x` / `1.4.x`. |
|
|
503
|
+
| `@latest` | Always the most recent published version. |
|
|
504
|
+
| `@^1.4.0` | Caret range, just like in `package.json`. |
|
|
505
|
+
|
|
506
|
+
> **Production tip.** Pin a specific version for production traffic so
|
|
507
|
+
> a published patch can't surprise you. `@latest` is fine for
|
|
508
|
+
> playgrounds and internal tools.
|
|
509
|
+
|
|
510
|
+
> **Why not `+esm`?** jsDelivr's `/+esm` flag tries to auto-bundle on
|
|
511
|
+
> demand and trips on bare-specifier resolution for some packages —
|
|
512
|
+
> `Failed to resolve module specifier "preact"` is the common symptom.
|
|
513
|
+
> Loading the pre-bundled `elements.esm.js` avoids that entirely:
|
|
514
|
+
> built once at release time, immutable forever, every dep inlined.
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## Caching, versioning & self-hosting
|
|
519
|
+
|
|
520
|
+
Two delivery models. Pick by how much you control the URL the customer pastes.
|
|
521
|
+
|
|
522
|
+
### Option 1 — public CDN, pinned version (no infra; the default today)
|
|
523
|
+
|
|
524
|
+
Serve the published NPM package straight from **jsDelivr**, pinned to an exact
|
|
525
|
+
version — swap `<version>` for the release you want (the immutable pinning the live demos use):
|
|
526
|
+
|
|
527
|
+
```html
|
|
528
|
+
<script type="module" async src="https://cdn.jsdelivr.net/npm/@helpai/elements@<version>/elements-web-component.esm.js"></script>
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
- jsDelivr serves a pinned version as `Cache-Control: public, max-age=31536000,
|
|
532
|
+
immutable` — cached for a year, global edge, free.
|
|
533
|
+
- **Always pin an exact version** for production. `@latest` / ranges (`@0.56`)
|
|
534
|
+
are mutable (jsDelivr revalidates ~daily) **and** auto-update — a published
|
|
535
|
+
patch can change behavior under you.
|
|
536
|
+
- Trade-off: the customer must **bump the version in their snippet to update**,
|
|
537
|
+
and every release re-downloads the whole ~107 KB (one self-contained file).
|
|
538
|
+
- Speed it up on pages you control: `<link rel="preconnect"
|
|
539
|
+
href="https://cdn.jsdelivr.net" crossorigin>` (and optionally `rel="modulepreload"`).
|
|
540
|
+
|
|
541
|
+
### Option 2 — your own CDN behind a stable URL (recommended; simplest for customers)
|
|
542
|
+
|
|
543
|
+
Point a CDN (`cdn.help.ai`, a Cloudflare/CloudFront origin, …) at the built
|
|
544
|
+
`dist/<brand>/` assets and give customers **one URL that never changes**:
|
|
545
|
+
|
|
546
|
+
```html
|
|
547
|
+
<!-- The customer pastes this ONCE and never touches it again. -->
|
|
548
|
+
<script src="https://cdn.help.ai/sdk/js/elements/latest/elements.js" data-public-key="agent_pk_..." data-ai-agent-deployment-id="01900000-0000-7000-8000-000000000000" defer></script>
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
You ship updates **behind** that stable URL — the customer's page is frozen. The
|
|
552
|
+
caching rule is the whole trick:
|
|
553
|
+
|
|
554
|
+
| Asset | `Cache-Control` | Why |
|
|
555
|
+
| ------------------------------------- | -------------------------------------- | -------------------------------------------- |
|
|
556
|
+
| The stable entry (`elements.js`) | `public, max-age=300, must-revalidate` | Short TTL so a deploy propagates in minutes. |
|
|
557
|
+
| Content-hashed chunks (`*.<hash>.js`) | `public, max-age=31536000, immutable` | Hash in the name → safe to cache forever. |
|
|
558
|
+
|
|
559
|
+
Plus Brotli/Gzip, HTTP/2+3, and (optionally) `integrity="sha384-…"` SRI hashes.
|
|
560
|
+
|
|
561
|
+
### Automatic chunking (zero customer changes)
|
|
562
|
+
|
|
563
|
+
The value of Option 2 is the **stable URL** above — the customer's snippet never
|
|
564
|
+
changes and you own caching + region. Chunking is a further, _optional_ step on
|
|
565
|
+
top (usually **not** worth it — see the caveat below). With **content-hashed
|
|
566
|
+
chunk filenames served from a version-less path**, an unchanged chunk keeps the
|
|
567
|
+
**same URL** across releases, so the browser reuses it — only the chunks that
|
|
568
|
+
actually changed re-download. To get there:
|
|
569
|
+
|
|
570
|
+
1. Build ESM with code-splitting on (esbuild/tsdown `splitting: true`, `format:
|
|
571
|
+
"esm"`) and `[hash]` filenames. The thin entry imports the chunks.
|
|
572
|
+
2. Add lazy `import()` boundaries at the **optional surfaces** — messenger tabs
|
|
573
|
+
(Home/Help/News), forms/HITL, voice. esbuild emits each as its own chunk and
|
|
574
|
+
hoists shared code (e.g. Preact) into a common chunk automatically. Core chat
|
|
575
|
+
loads first → smaller first paint; each feature caches independently.
|
|
576
|
+
3. The customer keeps the single stable `<script>` — all splitting, hashing, and
|
|
577
|
+
cache invalidation happen behind it. No snippet change, ever.
|
|
578
|
+
|
|
579
|
+
> **Why this needs your own CDN — not jsDelivr.** On jsDelivr the version lives in
|
|
580
|
+
> the path (`/@0.56.2/…`). When you ship `0.56.3` **every** chunk URL changes —
|
|
581
|
+
> even a byte-identical vendor chunk gets `/@0.56.3/vendor.<hash>.js`, a guaranteed
|
|
582
|
+
> cache miss. So splitting on a version-pinned CDN trades one request for many of
|
|
583
|
+
> the same total bytes — _worse_. Cross-release chunk caching requires
|
|
584
|
+
> **content-addressed URLs where the version is not in the path**, i.e. a CDN you
|
|
585
|
+
> control. (Splitting still helps **first-paint** on either, via lazy features.)
|
|
586
|
+
|
|
587
|
+
> **Is it worth it? (usually no.)** We built and measured this for the `@helpai/elements`
|
|
588
|
+
> bundle and reverted it: only ~10 % of an ~107 KB gz bundle is lazy-able (the rest is
|
|
589
|
+
> core chat that loads on first paint regardless), and the lazy `import()` needed to
|
|
590
|
+
> create a chunk is **inlined** into the single-file builds — so it _adds_ ~1 KB there
|
|
591
|
+
> for no benefit. The cost lands on the builds that don't chunk. For a single
|
|
592
|
+
> self-contained file that's `async` + immutable-cached, the per-release re-download is
|
|
593
|
+
> a non-issue anyway. Reach for chunking only if your optional surfaces
|
|
594
|
+
> (tabs/forms/voice) grow large enough that lazy-loading them meaningfully shrinks first
|
|
595
|
+
> paint — and **measure with an esbuild metafile first**, don't split blind.
|
|
596
|
+
|
|
597
|
+
> **Data residency.** Self-hosting (Option 2) also lets you keep asset delivery in
|
|
598
|
+
> a chosen region; the runtime still calls the agent API at the configured
|
|
599
|
+
> `baseUrl`.
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## What ships in the bundle
|
|
604
|
+
|
|
605
|
+
- **Preact 11 + @preact/signals** — reactive rendering primitives,
|
|
606
|
+
~6 KB combined gzipped.
|
|
607
|
+
- **streaming-markdown** — incremental markdown renderer, ~3 KB.
|
|
608
|
+
- **Widget code** — chat transcript, composer, messenger-module tabs
|
|
609
|
+
(Home / Help / News), forms + HITL, config, themes — the bulk.
|
|
610
|
+
- **i18n** — built-in locale strings (EN + bundled translations).
|
|
611
|
+
- **CSS** — inlined into the bundle (no separate request).
|
|
612
|
+
|
|
613
|
+
Total **~107 KB gzipped** for the IIFE / standalone ESM (see the live
|
|
614
|
+
budgets in `.size-limit.json`, enforced in CI).
|
|
615
|
+
No external runtime dependencies. No React, no jQuery, no analytics
|
|
616
|
+
beacons. The bulk is widget code, **not** vendor — relevant when you
|
|
617
|
+
weigh chunking (see [Caching, versioning & self-hosting](#caching-versioning--self-hosting)).
|
|
618
|
+
|
|
619
|
+
---
|
|
620
|
+
|
|
621
|
+
## Next
|
|
622
|
+
|
|
623
|
+
- [Configuration](./configuration.md) — every option explained with examples.
|
|
624
|
+
- [Theming & customization](./theming.md) — matching your brand.
|
|
625
|
+
- [Framework integrations](./frameworks.md) — framework-specific recipes.
|
|
626
|
+
- [Troubleshooting](./troubleshooting.md) — "my widget isn't showing up".
|