@clivly/chat-widget 0.7.2 → 0.7.4
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 +202 -93
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,128 +1,199 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@clivly/chat-widget`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An embeddable chat surface for your app's visitors. Messages land in your Clivly
|
|
4
|
+
workspace under **Conversations**, where your team replies in real time.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
> **New here? Start with
|
|
7
|
+
> [Adding the chat widget](../../docs/guides/adding-the-chat-widget.md)** — the
|
|
8
|
+
> canonical end-to-end guide (widget record, session route, embed, operator
|
|
9
|
+
> inbox). This README is the API-level reference for the package's entrypoints.
|
|
10
|
+
|
|
11
|
+
> **Preview-quality surface.** The end-to-end path (SDK session handler,
|
|
12
|
+
> `/widget.js` serving, operator inbox) is implemented and covered by
|
|
13
|
+
> integration tests; treat it as design-partner quality while the conversations
|
|
14
|
+
> UX settles.
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
|
|
18
|
+
| Requirement | Version / note |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| Node.js | **≥ 22** |
|
|
21
|
+
| `react` / `react-dom` | **≥ 18** — peer dependencies, for the React entrypoints only. The script-tag build bundles its own |
|
|
22
|
+
| **A backend** | Required. The widget never holds your API key; it calls a route on your origin that does. Static-only sites (plain HTML, WordPress, Webflow) cannot install it |
|
|
23
|
+
| A widget record | Created in the dashboard under **Settings → Chat widget** |
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
6
26
|
|
|
7
27
|
```bash
|
|
8
28
|
npm install @clivly/chat-widget
|
|
9
29
|
```
|
|
10
30
|
|
|
11
|
-
|
|
31
|
+
Published on npm's `latest` tag. Use `@next` only for prerelease testing.
|
|
32
|
+
|
|
33
|
+
The **host-side session route** comes from `clivly/sdk`, not from this package —
|
|
34
|
+
see [Host-side pairing](#host-side-pairing) below. `npx clivly add widget`
|
|
35
|
+
scaffolds it for you.
|
|
36
|
+
|
|
37
|
+
## Five-minute quick start
|
|
12
38
|
|
|
13
39
|
```bash
|
|
14
|
-
|
|
40
|
+
npx clivly add widget # scaffolds the session route on your backend
|
|
41
|
+
npm install @clivly/chat-widget
|
|
15
42
|
```
|
|
16
43
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
44
|
+
```tsx
|
|
45
|
+
import { ChatWidget, createSessionFetcher } from "@clivly/chat-widget";
|
|
46
|
+
|
|
47
|
+
<ChatWidget
|
|
48
|
+
widgetId="support"
|
|
49
|
+
getSession={createSessionFetcher({
|
|
50
|
+
sessionUrl: "/api/clivly/widget/session",
|
|
51
|
+
})}
|
|
52
|
+
/>;
|
|
53
|
+
```
|
|
21
54
|
|
|
22
|
-
|
|
23
|
-
|
|
55
|
+
`widgetId` is the **public slug** you set on the widget record — it is not a
|
|
56
|
+
credential. `sessionUrl` must be the path where you mounted the session route on
|
|
57
|
+
your own origin; `/api/clivly/widget/session` is what `clivly add widget`
|
|
58
|
+
writes.
|
|
24
59
|
|
|
25
60
|
## Entrypoints
|
|
26
61
|
|
|
62
|
+
This package exposes four stable entrypoints with different intended use cases.
|
|
63
|
+
|
|
27
64
|
### `@clivly/chat-widget`
|
|
28
65
|
|
|
29
|
-
Use the package root for
|
|
66
|
+
Use the package root for app-side integration code.
|
|
30
67
|
|
|
31
|
-
|
|
68
|
+
It exports:
|
|
32
69
|
|
|
33
|
-
-
|
|
34
|
-
- `ChatLauncher`
|
|
35
|
-
- `ChatPanel`
|
|
36
|
-
- `ConversationView`
|
|
37
|
-
- `MessageComposer`
|
|
38
|
-
- `PreChatForm`
|
|
70
|
+
- React widget components
|
|
39
71
|
- `createSessionFetcher()`
|
|
40
72
|
- `createClivlyTransport()`
|
|
41
|
-
-
|
|
42
|
-
- `createLoopbackTransport()`
|
|
43
|
-
- `mountChatWidget()`
|
|
44
|
-
- `mountChatWidgetScript()`
|
|
45
|
-
- `autoMountChatWidgetScript()`
|
|
73
|
+
- side-effect-free mount helpers
|
|
46
74
|
|
|
47
75
|
Example:
|
|
48
76
|
|
|
49
77
|
```ts
|
|
50
78
|
import {
|
|
51
|
-
|
|
52
|
-
|
|
79
|
+
createSessionFetcher,
|
|
80
|
+
createClivlyTransport,
|
|
81
|
+
mountChatWidget,
|
|
53
82
|
} from "@clivly/chat-widget";
|
|
83
|
+
|
|
84
|
+
const getSession = createSessionFetcher({
|
|
85
|
+
sessionUrl: "/api/clivly/widget/session",
|
|
86
|
+
});
|
|
54
87
|
```
|
|
55
88
|
|
|
56
89
|
### `@clivly/chat-widget/browser`
|
|
57
90
|
|
|
58
|
-
|
|
59
|
-
exists. It also exports `mountChatWidget()`, `mountChatWidgetScript()`, and
|
|
60
|
-
`autoMountChatWidgetScript()`.
|
|
91
|
+
Use this entry for the browser auto-mount module build.
|
|
61
92
|
|
|
62
|
-
|
|
93
|
+
This entry:
|
|
63
94
|
|
|
64
|
-
|
|
65
|
-
|
|
95
|
+
- exports `mountChatWidget()`
|
|
96
|
+
- exports `mountChatWidgetScript()`
|
|
97
|
+
- exports `autoMountChatWidgetScript()`
|
|
98
|
+
- auto-mounts at module evaluation time when `document` exists
|
|
66
99
|
|
|
67
|
-
|
|
100
|
+
This is the module-side equivalent of the script-tag embed behavior.
|
|
68
101
|
|
|
69
|
-
|
|
70
|
-
CDN-style delivery.
|
|
102
|
+
### `@clivly/chat-widget/mount`
|
|
71
103
|
|
|
72
|
-
|
|
104
|
+
Use this entry when you want the mount helpers without the browser entry's
|
|
105
|
+
auto-mount side effect.
|
|
73
106
|
|
|
74
|
-
|
|
75
|
-
import { ChatWidget, createSessionFetcher } from "@clivly/chat-widget";
|
|
107
|
+
This is the safest browser/helper entry for host-controlled mounting.
|
|
76
108
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
109
|
+
It exports:
|
|
110
|
+
|
|
111
|
+
- `mountChatWidget()`
|
|
112
|
+
- `mountChatWidgetScript()`
|
|
113
|
+
- `autoMountChatWidgetScript()`
|
|
114
|
+
- `MountChatWidgetOptions`
|
|
115
|
+
- `ChatWidgetMount`
|
|
116
|
+
- `ScriptMountResult`
|
|
84
117
|
|
|
85
|
-
|
|
118
|
+
### `@clivly/chat-widget/script`
|
|
86
119
|
|
|
87
|
-
|
|
88
|
-
<script
|
|
89
|
-
src="https://api.clivly.com/widget.js"
|
|
90
|
-
data-widget-id="support"
|
|
91
|
-
data-session-url="/api/clivly/widget/session"
|
|
92
|
-
defer
|
|
93
|
-
></script>
|
|
94
|
-
```
|
|
120
|
+
This entry maps to the generated global/browser artifact:
|
|
95
121
|
|
|
96
|
-
|
|
122
|
+
- `dist/widget.global.js`
|
|
97
123
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
| `data-widget-id` | yes | Public widget slug from the Clivly dashboard |
|
|
101
|
-
| `data-session-url` | yes | Your backend route that mounts `createChatSessionHandler()` |
|
|
102
|
-
| `data-theme` | no | `auto`, `light`, or `dark` |
|
|
103
|
-
| `data-socket-url-template` | no | Legacy socket override; usually unnecessary |
|
|
124
|
+
Use it for script-tag delivery, CDN-style consumption, or any future server path
|
|
125
|
+
that serves the bundled widget directly.
|
|
104
126
|
|
|
105
127
|
## Theming
|
|
106
128
|
|
|
107
|
-
The widget ships light and dark palettes and follows the host by default.
|
|
129
|
+
The widget ships light and dark palettes and follows the host app by default.
|
|
130
|
+
For integration recipes (Tailwind, next-themes, a custom toggle) see
|
|
131
|
+
[the guide's theming section](../../docs/guides/adding-the-chat-widget.md#5-theming).
|
|
108
132
|
|
|
109
|
-
`theme`
|
|
133
|
+
### `theme`
|
|
110
134
|
|
|
111
135
|
| Value | Behaviour |
|
|
112
136
|
| --- | --- |
|
|
113
|
-
| `"auto"` | Follows `data-theme` on `<html>`, then `dark
|
|
114
|
-
| `"light"` / `"dark"` | Pinned
|
|
137
|
+
| `"auto"` *(default)* | Follows the host: `data-theme="dark"`/`"light"` on `<html>`, then a `dark`/`light` class on `<html>`, then `prefers-color-scheme`. Re-resolves live. |
|
|
138
|
+
| `"light"` / `"dark"` | Pinned. Host signals are ignored and nothing is observed. |
|
|
139
|
+
|
|
140
|
+
Set it as a prop (`<ChatWidget theme="dark" />`), as `data-theme` on the script
|
|
141
|
+
tag, or as `theme` in `mountChatWidget()` options. An unrecognised
|
|
142
|
+
`data-theme` value falls back to `"auto"` rather than throwing.
|
|
143
|
+
|
|
144
|
+
`mountChatWidgetScript()` also returns `setTheme(theme)` for hosts whose toggle
|
|
145
|
+
does not touch `<html>`:
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
const { setTheme } = mountChatWidgetScript(script);
|
|
149
|
+
setTheme("dark"); // pin
|
|
150
|
+
setTheme("auto"); // hand control back to the host
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The resolved theme is applied as `data-clivly-theme="light|dark"` on the widget
|
|
154
|
+
root, together with `color-scheme`, so native scrollbars, autofill, and the
|
|
155
|
+
caret follow the panel.
|
|
156
|
+
|
|
157
|
+
### Palette tokens
|
|
115
158
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
159
|
+
Every colour reads through a CSS custom property. React hosts can override any
|
|
160
|
+
of them inline — inline declarations beat both palette rules, so no specificity
|
|
161
|
+
work is needed:
|
|
119
162
|
|
|
120
|
-
|
|
121
|
-
|
|
163
|
+
```tsx
|
|
164
|
+
<ChatWidget style={{ "--clivly-chat-panel": "#0b0f19" }} widgetId="support" />
|
|
165
|
+
```
|
|
122
166
|
|
|
123
|
-
|
|
167
|
+
| Token | Light | Dark |
|
|
168
|
+
| --- | --- | --- |
|
|
169
|
+
| `--clivly-chat-accent` | `#185adb` | `#4f8cff` |
|
|
170
|
+
| `--clivly-chat-accent-contrast` | `#ffffff` | `#0b1220` |
|
|
171
|
+
| `--clivly-chat-panel` | `#ffffff` | `#111827` |
|
|
172
|
+
| `--clivly-chat-input` | `transparent` | `#0b1220` |
|
|
173
|
+
| `--clivly-chat-border` | `#d6dde8` | `#2a3546` |
|
|
174
|
+
| `--clivly-chat-text` | `#132238` | `#e8edf5` |
|
|
175
|
+
| `--clivly-chat-muted` | `#60738a` | `#93a2b8` |
|
|
176
|
+
| `--clivly-chat-visitor` | `#ecf3ff` | `#1e3a6b` |
|
|
177
|
+
| `--clivly-chat-operator` | `#f5f7fa` | `#1b2534` |
|
|
178
|
+
| `--clivly-chat-shadow` | `rgba(19, 34, 56, 0.18)` | `rgba(0, 0, 0, 0.5)` |
|
|
179
|
+
| `--clivly-chat-error-bg` | `#fff1f1` | `#2c1618` |
|
|
180
|
+
| `--clivly-chat-error-border` | `#f2c2c2` | `#5c2a2e` |
|
|
181
|
+
| `--clivly-chat-error-text` | `#9f2d2d` | `#ffb4b4` |
|
|
182
|
+
|
|
183
|
+
The widget record's `brandColor` overrides `--clivly-chat-accent` in **both**
|
|
184
|
+
themes — the widget will not silently lighten your brand. If your brand colour
|
|
185
|
+
is very dark it will be low-contrast on the dark panel; pick a lighter accent,
|
|
186
|
+
or override the token per theme in a React host.
|
|
187
|
+
|
|
188
|
+
Deep per-token overrides are not available on the script-tag path: it mounts
|
|
189
|
+
into a Shadow DOM, so host CSS cannot reach the widget's internals. Use
|
|
190
|
+
`brandColor` and the two themes there.
|
|
191
|
+
|
|
192
|
+
## Session bootstrap shape
|
|
193
|
+
|
|
194
|
+
The widget expects a session bootstrap endpoint that returns a
|
|
195
|
+
`WidgetSessionPayload`.
|
|
124
196
|
|
|
125
|
-
The widget expects a bootstrap endpoint that returns a `WidgetSessionPayload`.
|
|
126
197
|
Important fields include:
|
|
127
198
|
|
|
128
199
|
- `token`
|
|
@@ -132,21 +203,39 @@ Important fields include:
|
|
|
132
203
|
- `messages`
|
|
133
204
|
- optional `socketUrl`
|
|
134
205
|
|
|
135
|
-
When `socketUrl` is present, the widget prefers it over reconstructing
|
|
136
|
-
|
|
206
|
+
When `socketUrl` is present, the widget prefers it over reconstructing the
|
|
207
|
+
socket URL from a client-side template.
|
|
208
|
+
|
|
209
|
+
## Transport behavior
|
|
137
210
|
|
|
138
|
-
|
|
211
|
+
The package includes:
|
|
139
212
|
|
|
140
|
-
|
|
213
|
+
- `createClivlyTransport()` for real Clivly backends
|
|
214
|
+
- `createWebSocketTransport()` for explicit socket wiring
|
|
215
|
+
- `createLoopbackTransport()` for local UI/testing flows
|
|
141
216
|
|
|
142
|
-
|
|
217
|
+
The default browser mount behavior is:
|
|
218
|
+
|
|
219
|
+
1. use a caller-provided `createTransport` when present
|
|
143
220
|
2. otherwise prefer `session.socketUrl`
|
|
144
|
-
3. otherwise fall back to `socketUrlTemplate`
|
|
221
|
+
3. otherwise fall back to `socketUrlTemplate` when provided
|
|
222
|
+
|
|
223
|
+
## Build outputs
|
|
224
|
+
|
|
225
|
+
The package currently produces:
|
|
226
|
+
|
|
227
|
+
- `dist/index.*`
|
|
228
|
+
- `dist/browser.*`
|
|
229
|
+
- `dist/browser-mount.*`
|
|
230
|
+
- `dist/widget.global.js`
|
|
145
231
|
|
|
146
|
-
|
|
232
|
+
The `browser` and `browser-mount` outputs are built as standalone entrypoints.
|
|
147
233
|
|
|
148
|
-
|
|
149
|
-
|
|
234
|
+
## Host-side pairing
|
|
235
|
+
|
|
236
|
+
The host-side session handler is the bridge between a backend holding
|
|
237
|
+
`CLIVLY_SECRET_KEY` and the widget's `sessionUrl`. Import it from `clivly/sdk`,
|
|
238
|
+
which is available from the `clivly` package `clivly init` already installs:
|
|
150
239
|
|
|
151
240
|
```ts
|
|
152
241
|
import { createChatSessionHandler } from "clivly/sdk";
|
|
@@ -156,22 +245,42 @@ export const POST = createChatSessionHandler({
|
|
|
156
245
|
});
|
|
157
246
|
```
|
|
158
247
|
|
|
159
|
-
|
|
248
|
+
`@clivly/sdk` exports the same handler if you depend on it directly. Don't import
|
|
249
|
+
from `clivly` (the package root) — that entry is the CLI and is Node-only.
|
|
160
250
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
251
|
+
Use the handler rather than hand-writing the proxy: it puts `origin` in the
|
|
252
|
+
request body (the backend requires it there, and only falls back to the `Origin`
|
|
253
|
+
header), accepts `visitorToken: null` for a first-time visitor, and turns an
|
|
254
|
+
unreachable Clivly into a JSON `502` instead of a thrown `fetch`.
|
|
164
255
|
|
|
165
|
-
|
|
166
|
-
correctly, accepts `visitorToken: null` for first-time visitors, and returns a
|
|
167
|
-
clean JSON `502` when Clivly is unreachable.
|
|
256
|
+
## Verify the integration
|
|
168
257
|
|
|
169
|
-
|
|
258
|
+
1. Load a page that embeds the widget, from an origin in the widget record's
|
|
259
|
+
**allowed origins** list.
|
|
260
|
+
2. Open the launcher and submit the pre-chat form.
|
|
261
|
+
3. The conversation should appear in your Clivly workspace under
|
|
262
|
+
**Conversations** within a second or two.
|
|
170
263
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
264
|
+
## Common errors and troubleshooting
|
|
265
|
+
|
|
266
|
+
| Symptom | Cause | Fix |
|
|
267
|
+
| --- | --- | --- |
|
|
268
|
+
| Widget mounts but every session is `403` | The origin is not in the widget record's allowlist. It is **exact-match**: `https://example.com` does not cover `https://www.example.com` | Add every embedding origin, with scheme, under **Settings → Chat widget** |
|
|
269
|
+
| A brand-new widget refuses everything | An empty origin list permits nothing | Add at least one origin |
|
|
270
|
+
| `503 widget_not_configured` | The Clivly server has no `CLIVLY_WIDGET_TOKEN_SECRET` | Only relevant when self-hosting — set it, or set `CLIVLY_MASTER_KEY` |
|
|
271
|
+
| Session request returns an opaque `400` | The route was hand-written and omits `origin` from the **body**, or rejects `visitorToken: null` | Use `createChatSessionHandler` from `clivly/sdk` |
|
|
272
|
+
| `404` on the session request | `sessionUrl` does not match where you mounted the route | `clivly add widget` writes `/api/clivly/widget/session` — match it, or set `sessionUrl` to your path |
|
|
273
|
+
| Bundle throws on `node:fs` / `node:child_process` | Something imported the `clivly` package root | Import the handler from `clivly/sdk` |
|
|
274
|
+
| A returning visitor starts a fresh thread on another device | By design — conversations resume by opaque `visitorToken` in `localStorage`, never by the (unverified) email | Not a bug; resuming by email would leak a known customer's history |
|
|
275
|
+
| Brand colour looks wrong on the dark panel | `brandColor` overrides `--clivly-chat-accent` in **both** themes | Pick a lighter accent, or override the token per theme in a React host |
|
|
276
|
+
| Deep CSS overrides do nothing on the script-tag embed | It mounts into a Shadow DOM, so host CSS cannot reach it | Use `brandColor` and the two themes there, or switch to the React embed |
|
|
277
|
+
|
|
278
|
+
## Related documentation
|
|
279
|
+
|
|
280
|
+
- **[Adding the chat widget](../../docs/guides/adding-the-chat-widget.md)** — the canonical end-to-end guide
|
|
281
|
+
- [Connecting to Clivly Cloud](../../docs/guides/connecting-to-clivly-cloud.md) — the overall integration guide
|
|
282
|
+
- [`@clivly/sdk` README](../sdk/README.md) — `createChatSessionHandler` and the rest of the runtime
|
|
283
|
+
- [`clivly` CLI README](../cli/README.md) — `clivly add widget`
|
|
175
284
|
|
|
176
285
|
## License
|
|
177
286
|
|