@paramms/chat-widget 1.0.40 → 1.0.42
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 +161 -12
- package/dist/connection.d.ts +13 -0
- package/dist/embed.d.ts +132 -10
- package/dist/embed.js +44 -10
- package/dist/embed.js.map +1 -1
- package/dist/index.js +314 -323
- package/dist/index.js.map +1 -1
- package/dist/outbox.js +37 -14
- package/dist/outbox.js.map +1 -1
- package/dist/react.js +187 -185
- package/dist/react.js.map +1 -1
- package/dist/renderer.d.ts +1 -2
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -8,7 +8,9 @@ Real-time embeddable chat widget for the Relay platform. Drop into any website w
|
|
|
8
8
|
npm install @paramms/chat-widget
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
## Quick start — CDN (no npm, no build)
|
|
11
|
+
## Quick start — CDN, ESM `mount()` (no npm, no build, manual container)
|
|
12
|
+
|
|
13
|
+
This is the ESM path — you control the mount point yourself. For a floating bubble on any site with zero JavaScript (or a config object), see [Script tag](#script-tag-any-website--plain-html-wordpress-shopify-phprailsdjangolaravel-or-literally-anything) below instead — it's usually simpler unless you specifically need to place the widget inline in your own container (`data-relay-*`/`Relay('boot', ...)` don't support choosing a container element; this ESM form does, via `el`).
|
|
12
14
|
|
|
13
15
|
```html
|
|
14
16
|
<div id="chat"></div>
|
|
@@ -111,9 +113,157 @@ Prefer a floating bubble that opens the same app in a panel? `<ChatAppLauncher
|
|
|
111
113
|
|
|
112
114
|
> **Multi-tenancy note:** conversations are created and keyed under the tenant the server resolves *from* `profileId` — guests can't spoof it. For **reading your own inbox**, `ChatApp`/`ChatAppLauncher` (and `useRelayChatList`/`mountChatList`/`GET /conversations/mine`) also accept a **`tenantId`** directly: it lists every conversation the user has with that business across all of its chatrooms, and is equally safe because the list is always self-scoped to the caller's own identity. `scope="tenant"` / `tenantId` never crosses into another business's data.
|
|
113
115
|
|
|
116
|
+
## Script tag (any website — plain HTML, WordPress, Shopify, PHP/Rails/Django/Laravel, or literally anything)
|
|
117
|
+
|
|
118
|
+
This is the path for every site that isn't React: one `<script>` tag, no build step, no framework. It works two ways — pick based on how much JavaScript you're willing to write:
|
|
119
|
+
|
|
120
|
+
- **Zero-JavaScript** — `data-relay-*` attributes right on the `<script>` tag. This is the one that matters most for a plain-HTML site, a WordPress block, or a no-code builder, because it's the *only* option when there's no JavaScript on the page at all — a server template just fills in attribute values.
|
|
121
|
+
- **Full config** — a JS object (`window.relaySettings = {...}` or `Relay('boot', {...})`, same shape either way). Needed the moment you want something an HTML attribute can't hold: a nested object, an array, a function, or changing the widget's identity *after* the page has loaded (e.g. once a visitor logs in).
|
|
122
|
+
|
|
123
|
+
Both configure the exact same widget with the exact same field names as the React components above — `profileId`, `userName`/`userEmail`/`userAvatar`, `contextTitle`/`contextSubtitle`/`contextStatus` (or `listingTitle`/`listingMeta`/`listingPrice`/`listingStatus` for a marketplace-style card), `accent`, `launcher`, `position`, `launcherMessage`. If you already know the React props, you already know these.
|
|
124
|
+
|
|
125
|
+
### Zero-JavaScript — `data-relay-*` attributes
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<script
|
|
129
|
+
async
|
|
130
|
+
src="https://relay.paramms.com/embed.js"
|
|
131
|
+
data-relay-app="YOUR_PROFILE_ID"
|
|
132
|
+
data-relay-user="user_123"
|
|
133
|
+
data-relay-user-name="Jane Doe"
|
|
134
|
+
data-relay-user-email="jane@example.com"
|
|
135
|
+
data-relay-accent="#4F63F5"
|
|
136
|
+
data-relay-position="bottom-right"
|
|
137
|
+
></script>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Drop that before `</body>` and you have a working floating bubble — no other JavaScript needed anywhere on the page. Every attribute is optional except `data-relay-app`.
|
|
141
|
+
|
|
142
|
+
| Attribute | Matches field | Notes |
|
|
143
|
+
|---|---|---|
|
|
144
|
+
| `data-relay-app` | `profileId` | **Required.** `data-relay-profile-id` works identically — same thing, matches the React prop name |
|
|
145
|
+
| `data-relay-user` | `userId` | Unauthenticated stable id. Omit for an anonymous guest |
|
|
146
|
+
| `data-relay-token` | `token` | Signed ES256 JWT — the production identity tier |
|
|
147
|
+
| `data-relay-user-name` | `userName` | Shown to agents |
|
|
148
|
+
| `data-relay-user-email` | `userEmail` | Shown to agents; also powers the offline email fallback |
|
|
149
|
+
| `data-relay-user-avatar` | `userAvatar` | Shown to agents |
|
|
150
|
+
| `data-relay-listing` | `listingId` | Sugar for `subjectId: "listing_<id>"` |
|
|
151
|
+
| `data-relay-context-title` / `-subtitle` / `-status` | `contextTitle` / `contextSubtitle` / `contextStatus` | Context card (general — an order, ticket, booking) |
|
|
152
|
+
| `data-relay-listing-title` / `-meta` / `-price` / `-status` | `listingTitle` / `listingMeta` / `listingPrice` / `listingStatus` | Marketplace card (a specific item — price + status badge). Use these OR the `context-*` set, both build the same card |
|
|
153
|
+
| `data-relay-accent` | `accent` | Brand colour hex |
|
|
154
|
+
| `data-relay-url` | `url` | Only needed for self-hosted Relay |
|
|
155
|
+
| `data-relay-position` | `position` | `bottom-right` \| `bottom-left` |
|
|
156
|
+
| `data-relay-launcher` | `launcher` | Set to `"false"` for an inline (non-floating) widget |
|
|
157
|
+
| `data-relay-launcher-message` / `data-relay-launcher-subtitle` | `launcherMessage` | Two flat attributes combine into the teaser card — see note below |
|
|
158
|
+
| `data-relay-target` | `el` | CSS selector for an existing element to mount into. Ignored in launcher mode |
|
|
159
|
+
| `data-relay-height` | `height` | Inline container height. Ignored in launcher mode |
|
|
160
|
+
| `data-relay-inbox` | `inbox` | `"true"` adds a back-chevron → full conversation list. Requires `data-relay-launcher="false"` — see the note below the table |
|
|
161
|
+
| `data-relay-inbox-start` | `inboxStart` | `"true"` opens **directly on the conversation list** instead of a single thread — for a dedicated "Messages" page. Implies `inbox`. Requires `data-relay-launcher="false"` |
|
|
162
|
+
| `data-relay-inbox-scope` | `inboxScope` | `tenant` (default) \| `profile` |
|
|
163
|
+
| `data-relay-tenant-id` | `tenantId` | Your business id. Lists threads across **all** your chatrooms without naming one — use instead of `data-relay-app` on an inbox page |
|
|
164
|
+
|
|
165
|
+
**What's NOT available as an attribute:** `user`/`subject` as nested objects, `quickReplies`, `i18n`, and `refreshToken` — an HTML attribute can only hold a string, so these need the JS-object form below. (`launcherMessage`'s two attributes are a workaround for exactly this: the React prop takes one `{ title, subtitle }` object, which an attribute can't express, so it's split into two flat attributes that get recombined.)
|
|
166
|
+
|
|
167
|
+
**`inbox` requires `data-relay-launcher="false"`** — same limit React has (a launcher+inbox combination needs a different component there, `ChatAppLauncher`); with the default floating launcher, `data-relay-inbox` is silently ignored.
|
|
168
|
+
|
|
169
|
+
**A whole "Messages" page, zero JavaScript.** `data-relay-inbox` alone only puts the list *behind* a back-chevron — the widget still lands on one thread, which is right for a product page and wrong for a page whose entire job is the inbox. `data-relay-inbox-start` makes the list the landing view, and `data-relay-tenant-id` spans every chatroom you run:
|
|
170
|
+
|
|
171
|
+
```html
|
|
172
|
+
<div id="relay-inbox" style="height:600px"></div>
|
|
173
|
+
|
|
174
|
+
<script
|
|
175
|
+
async
|
|
176
|
+
src="https://relay.paramms.com/embed.js"
|
|
177
|
+
data-relay-tenant-id="acme"
|
|
178
|
+
data-relay-inbox-start="true"
|
|
179
|
+
data-relay-launcher="false"
|
|
180
|
+
data-relay-target="#relay-inbox"
|
|
181
|
+
data-relay-user="user_123"
|
|
182
|
+
></script>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Tapping a row opens that conversation against **its own** chatroom; its back-chevron returns to the list. There's no ✕ in this mode — the list is the root view, so there'd be nothing behind it to close to. ✎ compose opens against your default chatroom (the server reports which). This is the script-tag equivalent of React's `<ChatApp tenantId={...} />`.
|
|
186
|
+
|
|
187
|
+
### Full config — JS object
|
|
188
|
+
|
|
189
|
+
Same field names, all in one place, same object whether it's set on page load or reacts to something happening later:
|
|
190
|
+
|
|
191
|
+
```html
|
|
192
|
+
<script async src="https://relay.paramms.com/embed.js"></script>
|
|
193
|
+
<script>
|
|
194
|
+
window.relaySettings = {
|
|
195
|
+
profileId: 'YOUR_PROFILE_ID', // required — your chatroom id
|
|
196
|
+
url: undefined, // optional — only for self-hosted Relay
|
|
197
|
+
apiUrl: undefined, // optional — only if REST lives on a different origin than the socket
|
|
198
|
+
|
|
199
|
+
token: undefined, // optional — signed ES256 JWT, production identity (wins over userId)
|
|
200
|
+
userId: 'user_123', // optional — unauthenticated stable id; omit both → anonymous guest
|
|
201
|
+
refreshToken: () => fetchNewToken(), // optional — called when a signed token expires
|
|
202
|
+
|
|
203
|
+
userName: 'Jane Doe', // optional — shown to agents, not identity
|
|
204
|
+
userEmail: 'jane@example.com', // optional — also powers the offline email fallback
|
|
205
|
+
userAvatar: 'https://…/jane.png', // optional
|
|
206
|
+
// or, if it's easier to build one object: user: { name, email, avatar }
|
|
207
|
+
|
|
208
|
+
subjectId: undefined, // optional — pins a dedicated thread; usually just use listingId below
|
|
209
|
+
listingId: '4821', // optional — sugar for subjectId: "listing_4821"
|
|
210
|
+
contextTitle: 'Order #4821', // optional — context card title (general use)
|
|
211
|
+
contextSubtitle: 'Placed Mar 3 · $129.00', // optional — context card subtitle
|
|
212
|
+
contextStatus: 'Shipped', // optional — status badge
|
|
213
|
+
// or, for a marketplace-style card: listingTitle / listingMeta / listingPrice / listingStatus
|
|
214
|
+
// or build the card yourself: subject: { title, subtitle, tags, status }
|
|
215
|
+
|
|
216
|
+
accent: '#4F63F5', // optional — brand colour
|
|
217
|
+
launcher: true, // optional — floating bubble vs inline; default true
|
|
218
|
+
position: 'bottom-right', // optional — 'bottom-right' | 'bottom-left'
|
|
219
|
+
launcherMessage: { title: 'Questions? Chat with us', subtitle: 'Start a conversation' }, // optional — a bare string also works (title only)
|
|
220
|
+
|
|
221
|
+
quickReplies: ['Track my order', 'Return an item'], // optional — reply chips above the input
|
|
222
|
+
i18n: { send: 'Enviar' }, // optional — UI string overrides
|
|
223
|
+
translateLang: undefined, // optional — auto-translate incoming messages (ISO code)
|
|
224
|
+
|
|
225
|
+
// Inline placement (ignored in launcher mode — a floating bubble is a
|
|
226
|
+
// fixed popup mount() owns, not something placed at a point in the page):
|
|
227
|
+
el: '#chat', // optional — CSS selector or element; omit → an auto-created host
|
|
228
|
+
height: '600px', // optional — inline container height
|
|
229
|
+
inbox: false, // optional — back-chevron → full conversation list (inline only, see below)
|
|
230
|
+
inboxScope: 'tenant', // optional — 'tenant' (default, all your chatrooms) | 'profile' (this one only)
|
|
231
|
+
}
|
|
232
|
+
</script>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`Relay('boot', {...})` (below) takes this exact same object — use whichever form fits how the page is built.
|
|
236
|
+
|
|
237
|
+
**`inbox` matches React exactly, including its one limit:** it only works with `launcher: false`. A floating launcher bubble is a fixed-size popup the widget owns; React itself needs a *different* component (`ChatAppLauncher`) for a launcher-with-inbox experience, and the script-tag path draws the same line. With `inbox: true` and `launcher: false`, the widget's header shows a back-chevron; tapping it swaps to the full conversation list (reusing the same list engine the dashboard's own inbox runs on); tapping a row opens that conversation; a ✕ in the list view returns to this widget's original thread.
|
|
238
|
+
|
|
239
|
+
### Commands — the same object, applied later
|
|
240
|
+
|
|
241
|
+
For identity that arrives after the page loads (a visitor logs in), or SPA-style navigation between pages without a full reload, call `Relay(...)` directly instead of (or in addition to) setting `window.relaySettings`. Same field names as above — `Relay('boot'/'identify'/'update', {...})` all take a `RelaySettings` object, just at a different moment:
|
|
242
|
+
|
|
243
|
+
```html
|
|
244
|
+
<script async src="https://relay.paramms.com/embed.js"></script>
|
|
245
|
+
<script>
|
|
246
|
+
Relay('boot', { profileId: 'YOUR_PROFILE_ID' }) // mount immediately, anonymous
|
|
247
|
+
|
|
248
|
+
// later, once the visitor logs in:
|
|
249
|
+
Relay('identify', { userId: currentUser.id, userName: currentUser.name }) // merges their guest history in
|
|
250
|
+
|
|
251
|
+
// on navigation to a different listing/order page:
|
|
252
|
+
Relay('update', { listingId: newListing.id, listingTitle: newListing.title })
|
|
253
|
+
|
|
254
|
+
// on logout:
|
|
255
|
+
Relay('shutdown') // removes the widget + local session
|
|
256
|
+
</script>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`Relay(...)` is queue-safe — calls made before `embed.js` finishes loading are never lost. `identify`/`update` merge onto whatever's currently running (a fresh `boot` replaces it entirely).
|
|
260
|
+
|
|
261
|
+
**Network requirement:** if the host page sets a Content-Security-Policy, it must allow the relay connection — `connect-src https://api.paramms.com wss://api.paramms.com;` (both are separate origins to the browser; include both), or your self-hosted equivalent.
|
|
262
|
+
|
|
114
263
|
### Old section below (kept for reference)
|
|
115
264
|
|
|
116
265
|
```tsx
|
|
266
|
+
|
|
117
267
|
'use client'
|
|
118
268
|
import { ChatWidget } from '@paramms/chat-widget/react'
|
|
119
269
|
|
|
@@ -214,16 +364,7 @@ arrives automatically over the socket. Pass `launcherMessage` only to override
|
|
|
214
364
|
the dashboard value for one embed (it also renders instantly, before the
|
|
215
365
|
socket connects).
|
|
216
366
|
|
|
217
|
-
Script-tag embeds use data
|
|
218
|
-
|
|
219
|
-
```html
|
|
220
|
-
<script
|
|
221
|
-
src="https://relay.paramms.com/embed.js"
|
|
222
|
-
data-relay-app="YOUR_PROFILE_ID"
|
|
223
|
-
data-relay-launcher-message="Questions? Chat with us"
|
|
224
|
-
data-relay-launcher-subtitle="Start a conversation"
|
|
225
|
-
></script>
|
|
226
|
-
```
|
|
367
|
+
Script-tag embeds use `data-relay-launcher-message` / `data-relay-launcher-subtitle` (or the `launcherMessage: {title,subtitle}` object in JS-object form) — see the [Script tag](#script-tag-any-website--plain-html-wordpress-shopify-phprailsdjangolaravel-or-literally-anything) section above for the full reference.
|
|
227
368
|
|
|
228
369
|
## Internationalisation
|
|
229
370
|
|
|
@@ -234,12 +375,18 @@ Script-tag embeds use data attributes:
|
|
|
234
375
|
i18n={{
|
|
235
376
|
placeholder: 'Écrivez un message…',
|
|
236
377
|
send: 'Envoyer',
|
|
237
|
-
offline: 'Nous sommes
|
|
378
|
+
offline: 'Nous sommes absents pour le moment', // the away notice
|
|
238
379
|
poweredBy: '', // empty string hides the footer
|
|
239
380
|
}}
|
|
240
381
|
/>
|
|
241
382
|
```
|
|
242
383
|
|
|
384
|
+
`offline` is the fallback text for the **away notice** shown above the composer
|
|
385
|
+
outside the chatroom's office hours. It never blocks anything: guests can always
|
|
386
|
+
send, the message is delivered like any other, and an agent replies when they're
|
|
387
|
+
back. The chatroom's own "offline message" (dashboard → *Widget message &
|
|
388
|
+
availability*) takes precedence over this string.
|
|
389
|
+
|
|
243
390
|
RTL is detected automatically for Arabic, Hebrew, Persian and Urdu browsers.
|
|
244
391
|
|
|
245
392
|
## All options
|
|
@@ -262,6 +409,8 @@ RTL is detected automatically for Arabic, Hebrew, Persian and Urdu browsers.
|
|
|
262
409
|
| `i18n` | `I18nStrings` | English | UI string overrides |
|
|
263
410
|
| `inbox` | `boolean` | `false` | Add a ‹ back in the chatroom header that opens the full `ChatApp` conversation list (inline widgets only) |
|
|
264
411
|
| `inboxScope` | `'tenant' \| 'profile'` | `'tenant'` | With `inbox`: list threads across all chatrooms (`tenant`) or just this one (`profile`) |
|
|
412
|
+
| `inboxStart` | `boolean` | `false` | Land ON the conversation list instead of a single thread (a dedicated "Messages" page). Implies `inbox`; requires `launcher: false`. Script-tag/embed only — in React use `<ChatApp/>` directly |
|
|
413
|
+
| `tenantId` | `string` | — | Script-tag/embed only. Your business id: lists threads across ALL your chatrooms with no chatroom named. Only meaningful with `inboxStart` — a single thread still needs a `profileId` |
|
|
265
414
|
|
|
266
415
|
## Development
|
|
267
416
|
|
package/dist/connection.d.ts
CHANGED
|
@@ -43,11 +43,24 @@ export declare class ConnectionManager {
|
|
|
43
43
|
* The outbox is bounded so a prolonged outage can't grow memory without limit
|
|
44
44
|
* — oldest queued frames are dropped past the cap. */
|
|
45
45
|
send(frame: ClientFrame): void;
|
|
46
|
+
/** How many frames are waiting to go out. Useful for a host that wants to
|
|
47
|
+
* show "message pending" state, and for asserting the outbox stays bounded. */
|
|
48
|
+
pendingCount(): number;
|
|
46
49
|
close(): void;
|
|
47
50
|
private handle;
|
|
48
51
|
private refreshing;
|
|
49
52
|
private fatal;
|
|
53
|
+
/** Release queued frames. When called from 'opened' we know the canonical
|
|
54
|
+
* conversation id the server just resolved us to, and queued `send` frames
|
|
55
|
+
* are retargeted to it. A manager only ever opens ONE conversation (its
|
|
56
|
+
* `opts.open`), so every queued send belongs to that thread by
|
|
57
|
+
* construction — but the id it was queued with can be STALE (queued against
|
|
58
|
+
* the previous session's conversation before a reconnect). Retargeting is a
|
|
59
|
+
* no-op in the normal case and rescues the message in the stale one. */
|
|
50
60
|
private flush;
|
|
61
|
+
/** Bounded enqueue — the cap lives here so EVERY path that queues respects
|
|
62
|
+
* it (a failed `raw` used to push straight onto the array, bypassing it). */
|
|
63
|
+
private queue;
|
|
51
64
|
private raw;
|
|
52
65
|
private onClosed;
|
|
53
66
|
}
|
package/dist/embed.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { type MountOptions, type UserInfo } from './index.js';
|
|
2
|
+
/** Everything an embedder can pass. All optional except `profileId`. Field
|
|
3
|
+
* names DELIBERATELY mirror the React props (`ChatWidgetProps` /
|
|
4
|
+
* `MarketplaceChatProps` in react.tsx) so the same mental model — and often
|
|
5
|
+
* the same field names — carries over whether you're using React or a plain
|
|
6
|
+
* script tag. Where a name changed over time the old one still works (see
|
|
7
|
+
* the `@deprecated` notes) — this is a published package embedded on live
|
|
8
|
+
* customer sites (WordPress plugin, Shopify theme block), so nothing here is
|
|
9
|
+
* ever removed, only added to. */
|
|
3
10
|
export interface RelaySettings {
|
|
4
|
-
/** The chatroom id (from your Relay dashboard). Required.
|
|
5
|
-
|
|
11
|
+
/** The chatroom id (from your Relay dashboard). Required. Matches the React
|
|
12
|
+
* `profileId` prop name. `appId` is the original alias — still works. */
|
|
6
13
|
profileId?: string;
|
|
14
|
+
/** @deprecated alias for `profileId` — kept working, `profileId` is now the
|
|
15
|
+
* documented name (matches React). */
|
|
16
|
+
appId?: string;
|
|
7
17
|
/** Relay server URL. Defaults to the hosted relay; set for self-hosted. */
|
|
8
18
|
url?: string;
|
|
9
19
|
apiUrl?: string;
|
|
@@ -12,27 +22,139 @@ export interface RelaySettings {
|
|
|
12
22
|
* both for an anonymous visitor. See EMBED.md. */
|
|
13
23
|
token?: string;
|
|
14
24
|
userId?: string;
|
|
15
|
-
/**
|
|
25
|
+
/** Called when a signed `token` is rejected (expired): return a fresh token
|
|
26
|
+
* from your backend to renew the session without a reload. Matches the
|
|
27
|
+
* React `refreshToken` prop. Only usable from `window.relaySettings` /
|
|
28
|
+
* `Relay('boot', ...)` (a function can't be expressed as an HTML
|
|
29
|
+
* attribute) — not available via `data-relay-*`. */
|
|
30
|
+
refreshToken?: () => Promise<string | null>;
|
|
31
|
+
/** Display info shown to agents (not identity) — matches the React
|
|
32
|
+
* `userName` / `userEmail` / `userAvatar` props. */
|
|
33
|
+
userName?: string;
|
|
34
|
+
userEmail?: string;
|
|
35
|
+
userAvatar?: string;
|
|
36
|
+
/** @deprecated flat aliases for `userName` / `userEmail` / `userAvatar` —
|
|
37
|
+
* kept working (the shipped Shopify integration used these names nested
|
|
38
|
+
* under `user`, see `user` below, which is the fix for that; these bare
|
|
39
|
+
* top-level fields predate that and still work standalone). */
|
|
16
40
|
name?: string;
|
|
17
41
|
email?: string;
|
|
18
42
|
avatar?: string;
|
|
19
|
-
/**
|
|
20
|
-
*
|
|
43
|
+
/** Same info as `userName`/`userEmail`/`userAvatar`, as one nested object —
|
|
44
|
+
* matches `MountOptions.user` / React's internal shape exactly, and is
|
|
45
|
+
* what a server-rendered snippet (e.g. Shopify Liquid, WordPress PHP) will
|
|
46
|
+
* most naturally emit: `user: { name: "...", email: "..." }`. Takes
|
|
47
|
+
* precedence over the flat fields if both are somehow given. */
|
|
48
|
+
user?: UserInfo;
|
|
49
|
+
/** Subject the chat is about (e.g. a marketplace listing). `listingId` is
|
|
50
|
+
* sugar for `subjectId: "listing_<id>"`. */
|
|
21
51
|
subjectId?: string;
|
|
22
52
|
listingId?: string;
|
|
53
|
+
/** Context-card fields — matches React's `ChatWidget` `contextTitle` /
|
|
54
|
+
* `contextSubtitle` / `contextStatus` props (a general "here's what this
|
|
55
|
+
* conversation is about" card: an order, ticket, booking, etc). */
|
|
56
|
+
contextTitle?: string;
|
|
57
|
+
contextSubtitle?: string;
|
|
58
|
+
contextStatus?: string;
|
|
59
|
+
/** Marketplace-card fields — matches React's `MarketplaceChat`
|
|
60
|
+
* `listingTitle` / `listingMeta` / `listingPrice` / `listingStatus` props
|
|
61
|
+
* (a specific-item card: price + status badge, e.g. "2019 Camry — $12,500
|
|
62
|
+
* — Available"). Use these OR `contextTitle`/etc — both build the same
|
|
63
|
+
* card, pick whichever vocabulary matches your use case. */
|
|
64
|
+
listingTitle?: string;
|
|
65
|
+
listingMeta?: string;
|
|
66
|
+
listingPrice?: number;
|
|
67
|
+
listingStatus?: string;
|
|
68
|
+
/** @deprecated original flat names for the context/marketplace card —
|
|
69
|
+
* kept working. `contextTitle`/`listingTitle` are now the documented
|
|
70
|
+
* names (matching the two React components). */
|
|
23
71
|
subjectTitle?: string;
|
|
24
72
|
subjectMeta?: string;
|
|
25
73
|
subjectPrice?: number;
|
|
26
74
|
subjectStatus?: string;
|
|
75
|
+
/** The context/marketplace card as one nested object, if you'd rather build
|
|
76
|
+
* it yourself than use the flat fields above — matches
|
|
77
|
+
* `MountOptions.subject` exactly. Takes precedence over every flat field
|
|
78
|
+
* above if given. */
|
|
79
|
+
subject?: {
|
|
80
|
+
title?: string;
|
|
81
|
+
subtitle?: string;
|
|
82
|
+
tags?: string[];
|
|
83
|
+
status?: string;
|
|
84
|
+
ownerLabel?: string;
|
|
85
|
+
};
|
|
86
|
+
/** Pre-set reply chips shown above the input — matches the React
|
|
87
|
+
* `quickReplies` prop. `window.relaySettings` / `Relay('boot', ...)` only
|
|
88
|
+
* (an array can't be expressed as a single `data-relay-*` attribute). */
|
|
89
|
+
quickReplies?: string[];
|
|
90
|
+
/** i18n string overrides — matches the React `i18n` prop. Same restriction
|
|
91
|
+
* as `quickReplies`: object, so JS-object form only. */
|
|
92
|
+
i18n?: MountOptions['i18n'];
|
|
27
93
|
/** Appearance. `launcher` defaults to true (a floating bubble). */
|
|
28
94
|
accent?: string;
|
|
29
95
|
launcher?: boolean;
|
|
30
96
|
position?: 'bottom-right' | 'bottom-left';
|
|
31
|
-
/** Launcher teaser ("optional message" above the bubble).
|
|
32
|
-
*
|
|
33
|
-
|
|
97
|
+
/** Launcher teaser ("optional message" above the bubble). Matches the React
|
|
98
|
+
* `launcherMessage` prop exactly: a bare string (title only), or
|
|
99
|
+
* `{ title, subtitle }`. `launcherSubtitle` below is a SEPARATE flat
|
|
100
|
+
* field kept only so `data-relay-launcher-message` /
|
|
101
|
+
* `data-relay-launcher-subtitle` (two HTML attributes — an attribute
|
|
102
|
+
* can't hold a nested object) can still combine into the same shape; in
|
|
103
|
+
* JS-object form just pass the object directly, same as React. Omit to
|
|
104
|
+
* use the chatroom's manifest value. */
|
|
105
|
+
launcherMessage?: string | {
|
|
106
|
+
title: string;
|
|
107
|
+
subtitle?: string;
|
|
108
|
+
};
|
|
109
|
+
/** @deprecated HTML-attribute-only companion to a string `launcherMessage`
|
|
110
|
+
* — see the note above. Prefer `launcherMessage: { title, subtitle }` in
|
|
111
|
+
* JS-object form. */
|
|
34
112
|
launcherSubtitle?: string;
|
|
35
113
|
translateLang?: string;
|
|
114
|
+
/** Mount INLINE into an existing element instead of the auto-created,
|
|
115
|
+
* body-appended host that the default floating launcher uses. A CSS
|
|
116
|
+
* selector string (works from `data-relay-target` too) or an element
|
|
117
|
+
* reference (JS-object form only). Ignored when `launcher` is true — same
|
|
118
|
+
* restriction as `height`/`inbox` below: a floating launcher panel is a
|
|
119
|
+
* fixed-size popup `mount()` owns, not something you place in the page. */
|
|
120
|
+
el?: string | HTMLElement;
|
|
121
|
+
/** Inline container height — matches the React `height` prop. Only applies
|
|
122
|
+
* when `launcher` is false/omitted. */
|
|
123
|
+
height?: string;
|
|
124
|
+
/** Adds a back-chevron to the widget that swaps it for the full
|
|
125
|
+
* conversation list — matches the React `ChatWidget`/`MarketplaceChat`
|
|
126
|
+
* `inbox` prop, INCLUDING its one limitation: not supported in launcher
|
|
127
|
+
* mode. (React itself requires a different component, `ChatAppLauncher`,
|
|
128
|
+
* for a launcher+inbox combination — same scope boundary here.) Tapping a
|
|
129
|
+
* row opens that conversation; its own back-chevron returns to the list; a
|
|
130
|
+
* ✕ in the list view returns to this widget's original single-thread
|
|
131
|
+
* view. Requires `launcher: false`. */
|
|
132
|
+
inbox?: boolean;
|
|
133
|
+
/** Open DIRECTLY on the conversation list instead of a single thread —
|
|
134
|
+
* what you want for a dedicated "Messages" page. `inbox: true` alone only
|
|
135
|
+
* adds a back-chevron to a single thread, so the list is reachable but
|
|
136
|
+
* never the landing view; that is the right default for a widget bolted
|
|
137
|
+
* onto a product page, and the wrong one for a page whose whole job is the
|
|
138
|
+
* inbox. Implies `inbox`. Requires `launcher: false` (same restriction as
|
|
139
|
+
* `inbox`), and `profileId` OR `tenantId`.
|
|
140
|
+
*
|
|
141
|
+
* With `inboxStart`, tapping a row opens that conversation and its own
|
|
142
|
+
* back-chevron returns to the list. There is no ✕ — the list IS the root
|
|
143
|
+
* view here, so there is nothing behind it to close back to. */
|
|
144
|
+
inboxStart?: boolean;
|
|
145
|
+
/** Inbox scope when `inbox` is set: `'tenant'` (default) lists the user's
|
|
146
|
+
* threads across ALL your chatrooms; `'profile'` limits it to this one. */
|
|
147
|
+
inboxScope?: 'tenant' | 'profile';
|
|
148
|
+
/** Your business id — lists the visitor's threads across ALL your chatrooms
|
|
149
|
+
* without naming one, and lets "new conversation" open against your default
|
|
150
|
+
* chatroom (the server reports it). This is the vanilla equivalent of
|
|
151
|
+
* React's `<ChatApp tenantId=… />`, which has always accepted a tenant with
|
|
152
|
+
* no profile; the script tag previously could not express that at all and
|
|
153
|
+
* hard-required a `profileId`.
|
|
154
|
+
*
|
|
155
|
+
* Only meaningful with `inboxStart` — a single-thread widget still needs a
|
|
156
|
+
* `profileId`, because a lone thread has to belong to a specific chatroom. */
|
|
157
|
+
tenantId?: string;
|
|
36
158
|
}
|
|
37
159
|
export type RelayCommand = 'boot' | 'update' | 'identify' | 'shutdown';
|
|
38
160
|
/** The public command dispatcher exposed as `window.Relay`. */
|