@customerhero/js 2.1.1 → 2.3.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 +19 -0
- package/dist/index.cjs +238 -5
- package/dist/index.d.cts +181 -2
- package/dist/index.d.ts +181 -2
- package/dist/index.js +232 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,6 +56,25 @@ chat.identify({
|
|
|
56
56
|
| `locale` | `string` | Widget locale (e.g. `"en"`, `"es"`). Auto-detected from browser if omitted. |
|
|
57
57
|
| `suggestedMessages` | `string[]` | Quick-reply options shown before the first message. |
|
|
58
58
|
|
|
59
|
+
### Appearance
|
|
60
|
+
|
|
61
|
+
| Option | Type | Description |
|
|
62
|
+
| ------------------------ | ----------------------------------- | --------------------------------------------------------------------------------- |
|
|
63
|
+
| `colorScheme` | `"auto" \| "light" \| "dark"` | `auto` follows the visitor's OS preference. Defaults to `light`. |
|
|
64
|
+
| `primaryColorDark` | `string` | Primary color used in dark mode. Only honoured when the effective scheme is dark. |
|
|
65
|
+
| `backgroundColorDark` | `string` | Background color used in dark mode. |
|
|
66
|
+
| `textColorDark` | `string` | Text color used in dark mode. |
|
|
67
|
+
| `size` | `"compact" \| "default" \| "large"` | Launcher diameter, panel dimensions, and base font size. |
|
|
68
|
+
| `cornerStyle` | `"soft" \| "rounded" \| "square"` | Panel border-radius preset. |
|
|
69
|
+
| `launcher.iconUrl` | `string` | Custom launcher icon URL (replaces the default chat-bubble glyph). |
|
|
70
|
+
| `launcher.label` | `string` | CTA label next to the launcher (turns the bubble into a pill). Max 60 chars. |
|
|
71
|
+
| `launcher.showOnlineDot` | `boolean` | Show a small green dot on the launcher when agents are available. |
|
|
72
|
+
| `offset.bottom` | `number` | Pixel offset from the bottom edge. 0–1000. Defaults to 20. |
|
|
73
|
+
| `offset.side` | `number` | Pixel offset from the side (mirrors `position`). 0–1000. Defaults to 20. |
|
|
74
|
+
| `zIndex` | `number` | Z-index override. Defaults to 99999. Capped at 2 000 000 000. |
|
|
75
|
+
|
|
76
|
+
Dark colors are never auto-derived from `primaryColor`/`backgroundColor`/`textColor` — set them explicitly when enabling dark or auto modes.
|
|
77
|
+
|
|
59
78
|
## License
|
|
60
79
|
|
|
61
80
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -20,8 +20,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
CORNER_RADIUS: () => CORNER_RADIUS,
|
|
23
24
|
CustomerHeroChat: () => CustomerHeroChat,
|
|
24
25
|
DEFAULTS: () => DEFAULTS,
|
|
26
|
+
SIZE_PRESETS: () => SIZE_PRESETS,
|
|
25
27
|
SUPPORTED_LOCALES: () => SUPPORTED_LOCALES,
|
|
26
28
|
ScreenshotCancelled: () => ScreenshotCancelled,
|
|
27
29
|
ScreenshotUnavailable: () => ScreenshotUnavailable,
|
|
@@ -29,10 +31,14 @@ __export(index_exports, {
|
|
|
29
31
|
captureScreenshot: () => captureScreenshot,
|
|
30
32
|
createTranslator: () => createTranslator,
|
|
31
33
|
detectLocale: () => detectLocale,
|
|
34
|
+
effectiveColors: () => effectiveColors,
|
|
32
35
|
evaluate: () => evaluate,
|
|
33
36
|
isRtlLocale: () => isRtlLocale,
|
|
37
|
+
panelRadius: () => panelRadius,
|
|
34
38
|
pickFire: () => pickFire,
|
|
35
39
|
resolveLocale: () => resolveLocale,
|
|
40
|
+
resolveScheme: () => resolveScheme,
|
|
41
|
+
sizePreset: () => sizePreset,
|
|
36
42
|
startTriggersRuntime: () => startTriggersRuntime
|
|
37
43
|
});
|
|
38
44
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -46,7 +52,29 @@ var DEFAULTS = {
|
|
|
46
52
|
position: "bottom-right",
|
|
47
53
|
placeholderText: "Type your message...",
|
|
48
54
|
welcomeMessage: "Hi! How can I help you today?",
|
|
49
|
-
title: "CustomerHero"
|
|
55
|
+
title: "CustomerHero",
|
|
56
|
+
// Appearance pack (B1–B6) defaults.
|
|
57
|
+
colorScheme: "light",
|
|
58
|
+
// Dark fallbacks used when the effective scheme is dark and no per-chatbot
|
|
59
|
+
// dark color is configured. Operators can — and should — override these.
|
|
60
|
+
primaryColorDark: "#A78BFA",
|
|
61
|
+
backgroundColorDark: "#0F172A",
|
|
62
|
+
textColorDark: "#E5E7EB",
|
|
63
|
+
size: "default",
|
|
64
|
+
cornerStyle: "rounded",
|
|
65
|
+
offsetBottom: 20,
|
|
66
|
+
offsetSide: 20,
|
|
67
|
+
zIndex: 99999
|
|
68
|
+
};
|
|
69
|
+
var SIZE_PRESETS = {
|
|
70
|
+
compact: { bubble: 48, width: 320, height: 480, fontSize: 13 },
|
|
71
|
+
default: { bubble: 56, width: 380, height: 520, fontSize: 14 },
|
|
72
|
+
large: { bubble: 64, width: 440, height: 600, fontSize: 15 }
|
|
73
|
+
};
|
|
74
|
+
var CORNER_RADIUS = {
|
|
75
|
+
soft: 8,
|
|
76
|
+
rounded: 16,
|
|
77
|
+
square: 0
|
|
50
78
|
};
|
|
51
79
|
|
|
52
80
|
// src/i18n/locales/en.ts
|
|
@@ -75,6 +103,8 @@ var en = {
|
|
|
75
103
|
attach_menu_open: "Add attachment",
|
|
76
104
|
attach_photo: "Choose file",
|
|
77
105
|
drop_files_here: "Drop files here",
|
|
106
|
+
incident_dismiss: "Dismiss",
|
|
107
|
+
incident_default_link_label: "Learn more",
|
|
78
108
|
attachment_unsupported_type: "Unsupported file type"
|
|
79
109
|
};
|
|
80
110
|
|
|
@@ -104,6 +134,8 @@ var es = {
|
|
|
104
134
|
attach_menu_open: "A\xF1adir adjunto",
|
|
105
135
|
attach_photo: "Elegir archivo",
|
|
106
136
|
drop_files_here: "Suelta los archivos aqu\xED",
|
|
137
|
+
incident_dismiss: "Descartar",
|
|
138
|
+
incident_default_link_label: "M\xE1s informaci\xF3n",
|
|
107
139
|
attachment_unsupported_type: "Tipo de archivo no admitido"
|
|
108
140
|
};
|
|
109
141
|
|
|
@@ -133,6 +165,8 @@ var ptBR = {
|
|
|
133
165
|
attach_menu_open: "Adicionar anexo",
|
|
134
166
|
attach_photo: "Escolher arquivo",
|
|
135
167
|
drop_files_here: "Solte os arquivos aqui",
|
|
168
|
+
incident_dismiss: "Dispensar",
|
|
169
|
+
incident_default_link_label: "Saiba mais",
|
|
136
170
|
attachment_unsupported_type: "Tipo de arquivo n\xE3o suportado"
|
|
137
171
|
};
|
|
138
172
|
|
|
@@ -162,6 +196,8 @@ var ptPT = {
|
|
|
162
196
|
attach_menu_open: "Adicionar anexo",
|
|
163
197
|
attach_photo: "Escolher ficheiro",
|
|
164
198
|
drop_files_here: "Largue os ficheiros aqui",
|
|
199
|
+
incident_dismiss: "Dispensar",
|
|
200
|
+
incident_default_link_label: "Saiba mais",
|
|
165
201
|
attachment_unsupported_type: "Tipo de ficheiro n\xE3o suportado"
|
|
166
202
|
};
|
|
167
203
|
|
|
@@ -191,6 +227,8 @@ var fr = {
|
|
|
191
227
|
attach_menu_open: "Ajouter une pi\xE8ce jointe",
|
|
192
228
|
attach_photo: "Choisir un fichier",
|
|
193
229
|
drop_files_here: "D\xE9posez les fichiers ici",
|
|
230
|
+
incident_dismiss: "Ignorer",
|
|
231
|
+
incident_default_link_label: "En savoir plus",
|
|
194
232
|
attachment_unsupported_type: "Type de fichier non pris en charge"
|
|
195
233
|
};
|
|
196
234
|
|
|
@@ -220,6 +258,8 @@ var de = {
|
|
|
220
258
|
attach_menu_open: "Anhang hinzuf\xFCgen",
|
|
221
259
|
attach_photo: "Datei ausw\xE4hlen",
|
|
222
260
|
drop_files_here: "Dateien hier ablegen",
|
|
261
|
+
incident_dismiss: "Schlie\xDFen",
|
|
262
|
+
incident_default_link_label: "Mehr erfahren",
|
|
223
263
|
attachment_unsupported_type: "Dateityp nicht unterst\xFCtzt"
|
|
224
264
|
};
|
|
225
265
|
|
|
@@ -249,6 +289,8 @@ var it = {
|
|
|
249
289
|
attach_menu_open: "Aggiungi allegato",
|
|
250
290
|
attach_photo: "Scegli file",
|
|
251
291
|
drop_files_here: "Trascina qui i file",
|
|
292
|
+
incident_dismiss: "Chiudi",
|
|
293
|
+
incident_default_link_label: "Scopri di pi\xF9",
|
|
252
294
|
attachment_unsupported_type: "Tipo di file non supportato"
|
|
253
295
|
};
|
|
254
296
|
|
|
@@ -278,6 +320,8 @@ var nl = {
|
|
|
278
320
|
attach_menu_open: "Bijlage toevoegen",
|
|
279
321
|
attach_photo: "Bestand kiezen",
|
|
280
322
|
drop_files_here: "Sleep bestanden hier",
|
|
323
|
+
incident_dismiss: "Sluiten",
|
|
324
|
+
incident_default_link_label: "Meer informatie",
|
|
281
325
|
attachment_unsupported_type: "Bestandstype niet ondersteund"
|
|
282
326
|
};
|
|
283
327
|
|
|
@@ -307,6 +351,8 @@ var pl = {
|
|
|
307
351
|
attach_menu_open: "Dodaj za\u0142\u0105cznik",
|
|
308
352
|
attach_photo: "Wybierz plik",
|
|
309
353
|
drop_files_here: "Upu\u015B\u0107 pliki tutaj",
|
|
354
|
+
incident_dismiss: "Zamknij",
|
|
355
|
+
incident_default_link_label: "Dowiedz si\u0119 wi\u0119cej",
|
|
310
356
|
attachment_unsupported_type: "Nieobs\u0142ugiwany typ pliku"
|
|
311
357
|
};
|
|
312
358
|
|
|
@@ -336,6 +382,8 @@ var tr = {
|
|
|
336
382
|
attach_menu_open: "Ek ekle",
|
|
337
383
|
attach_photo: "Dosya se\xE7",
|
|
338
384
|
drop_files_here: "Dosyalar\u0131 buraya b\u0131rak",
|
|
385
|
+
incident_dismiss: "Kapat",
|
|
386
|
+
incident_default_link_label: "Daha fazla bilgi",
|
|
339
387
|
attachment_unsupported_type: "Desteklenmeyen dosya t\xFCr\xFC"
|
|
340
388
|
};
|
|
341
389
|
|
|
@@ -365,6 +413,8 @@ var ar = {
|
|
|
365
413
|
attach_menu_open: "\u0625\u0636\u0627\u0641\u0629 \u0645\u0631\u0641\u0642",
|
|
366
414
|
attach_photo: "\u0627\u062E\u062A\u0631 \u0645\u0644\u0641\u064B\u0627",
|
|
367
415
|
drop_files_here: "\u0623\u0641\u0644\u062A \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0647\u0646\u0627",
|
|
416
|
+
incident_dismiss: "\u0625\u063A\u0644\u0627\u0642",
|
|
417
|
+
incident_default_link_label: "\u0627\u0639\u0631\u0641 \u0627\u0644\u0645\u0632\u064A\u062F",
|
|
368
418
|
attachment_unsupported_type: "\u0646\u0648\u0639 \u0627\u0644\u0645\u0644\u0641 \u063A\u064A\u0631 \u0645\u062F\u0639\u0648\u0645"
|
|
369
419
|
};
|
|
370
420
|
|
|
@@ -394,6 +444,8 @@ var ja = {
|
|
|
394
444
|
attach_menu_open: "\u6DFB\u4ED8\u30D5\u30A1\u30A4\u30EB\u3092\u8FFD\u52A0",
|
|
395
445
|
attach_photo: "\u30D5\u30A1\u30A4\u30EB\u3092\u9078\u629E",
|
|
396
446
|
drop_files_here: "\u30D5\u30A1\u30A4\u30EB\u3092\u3053\u3053\u306B\u30C9\u30ED\u30C3\u30D7",
|
|
447
|
+
incident_dismiss: "\u9589\u3058\u308B",
|
|
448
|
+
incident_default_link_label: "\u8A73\u7D30\u3092\u898B\u308B",
|
|
397
449
|
attachment_unsupported_type: "\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u306A\u3044\u30D5\u30A1\u30A4\u30EB\u5F62\u5F0F"
|
|
398
450
|
};
|
|
399
451
|
|
|
@@ -423,6 +475,8 @@ var ko = {
|
|
|
423
475
|
attach_menu_open: "\uCCA8\uBD80 \uD30C\uC77C \uCD94\uAC00",
|
|
424
476
|
attach_photo: "\uD30C\uC77C \uC120\uD0DD",
|
|
425
477
|
drop_files_here: "\uD30C\uC77C\uC744 \uC5EC\uAE30\uC5D0 \uB193\uAE30",
|
|
478
|
+
incident_dismiss: "\uB2EB\uAE30",
|
|
479
|
+
incident_default_link_label: "\uC790\uC138\uD788 \uC54C\uC544\uBCF4\uAE30",
|
|
426
480
|
attachment_unsupported_type: "\uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 \uD30C\uC77C \uD615\uC2DD"
|
|
427
481
|
};
|
|
428
482
|
|
|
@@ -452,6 +506,8 @@ var zhCN = {
|
|
|
452
506
|
attach_menu_open: "\u6DFB\u52A0\u9644\u4EF6",
|
|
453
507
|
attach_photo: "\u9009\u62E9\u6587\u4EF6",
|
|
454
508
|
drop_files_here: "\u5C06\u6587\u4EF6\u62D6\u653E\u5230\u6B64\u5904",
|
|
509
|
+
incident_dismiss: "\u5173\u95ED",
|
|
510
|
+
incident_default_link_label: "\u4E86\u89E3\u66F4\u591A",
|
|
455
511
|
attachment_unsupported_type: "\u4E0D\u652F\u6301\u7684\u6587\u4EF6\u7C7B\u578B"
|
|
456
512
|
};
|
|
457
513
|
|
|
@@ -481,6 +537,8 @@ var zhTW = {
|
|
|
481
537
|
attach_menu_open: "\u65B0\u589E\u9644\u4EF6",
|
|
482
538
|
attach_photo: "\u9078\u64C7\u6A94\u6848",
|
|
483
539
|
drop_files_here: "\u5C07\u6A94\u6848\u62D6\u653E\u5230\u6B64\u8655",
|
|
540
|
+
incident_dismiss: "\u95DC\u9589",
|
|
541
|
+
incident_default_link_label: "\u77AD\u89E3\u66F4\u591A",
|
|
484
542
|
attachment_unsupported_type: "\u4E0D\u652F\u63F4\u7684\u6A94\u6848\u985E\u578B"
|
|
485
543
|
};
|
|
486
544
|
|
|
@@ -984,7 +1042,14 @@ function startTriggersRuntime(options) {
|
|
|
984
1042
|
}
|
|
985
1043
|
|
|
986
1044
|
// src/client.ts
|
|
1045
|
+
function clampInt(value, min, max, fallback) {
|
|
1046
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
1047
|
+
return Math.max(min, Math.min(max, Math.trunc(value)));
|
|
1048
|
+
}
|
|
987
1049
|
function resolveConfig(userConfig, fetched) {
|
|
1050
|
+
const launcherUser = userConfig.launcher ?? {};
|
|
1051
|
+
const launcherFetched = fetched?.launcher ?? {};
|
|
1052
|
+
const offsetUser = userConfig.offset ?? {};
|
|
988
1053
|
return {
|
|
989
1054
|
chatbotId: userConfig.chatbotId,
|
|
990
1055
|
apiBase: userConfig.apiBase ?? DEFAULTS.apiBase,
|
|
@@ -997,12 +1062,78 @@ function resolveConfig(userConfig, fetched) {
|
|
|
997
1062
|
title: userConfig.title ?? fetched?.title ?? DEFAULTS.title,
|
|
998
1063
|
avatarUrl: userConfig.avatarUrl ?? fetched?.avatarUrl,
|
|
999
1064
|
suggestedMessages: userConfig.suggestedMessages ?? fetched?.suggestedMessages ?? [],
|
|
1000
|
-
stringOverrides: fetched?.stringOverrides
|
|
1065
|
+
stringOverrides: fetched?.stringOverrides,
|
|
1066
|
+
// Appearance pack. Color palette + size + corner style + launcher all
|
|
1067
|
+
// come from the server widget_config (with host-side override). The
|
|
1068
|
+
// *runtime* knobs — colorScheme, offset, zIndex — are host-only because
|
|
1069
|
+
// they depend on the page the widget is embedded in, not the chatbot.
|
|
1070
|
+
primaryColorDark: userConfig.primaryColorDark ?? fetched?.primaryColorDark,
|
|
1071
|
+
backgroundColorDark: userConfig.backgroundColorDark ?? fetched?.backgroundColorDark,
|
|
1072
|
+
textColorDark: userConfig.textColorDark ?? fetched?.textColorDark,
|
|
1073
|
+
size: userConfig.size ?? fetched?.size ?? DEFAULTS.size,
|
|
1074
|
+
cornerStyle: userConfig.cornerStyle ?? fetched?.cornerStyle ?? DEFAULTS.cornerStyle,
|
|
1075
|
+
launcher: {
|
|
1076
|
+
iconUrl: launcherUser.iconUrl ?? launcherFetched.iconUrl,
|
|
1077
|
+
label: launcherUser.label ?? launcherFetched.label,
|
|
1078
|
+
showOnlineDot: launcherUser.showOnlineDot ?? launcherFetched.showOnlineDot ?? false
|
|
1079
|
+
},
|
|
1080
|
+
colorScheme: userConfig.colorScheme ?? DEFAULTS.colorScheme,
|
|
1081
|
+
offset: {
|
|
1082
|
+
bottom: clampInt(offsetUser.bottom, 0, 1e3, DEFAULTS.offsetBottom),
|
|
1083
|
+
side: clampInt(offsetUser.side, 0, 1e3, DEFAULTS.offsetSide)
|
|
1084
|
+
},
|
|
1085
|
+
zIndex: clampInt(userConfig.zIndex, 0, 2e9, DEFAULTS.zIndex),
|
|
1086
|
+
// Defaults to true — the widget shows the attach button unless the
|
|
1087
|
+
// chatbot explicitly opts out via widget_config or the host passes
|
|
1088
|
+
// allowAttachments=false. Server still enforces the same flag at the
|
|
1089
|
+
// upload endpoint either way.
|
|
1090
|
+
allowAttachments: userConfig.allowAttachments ?? fetched?.allowAttachments ?? true
|
|
1001
1091
|
};
|
|
1002
1092
|
}
|
|
1093
|
+
function sanitizeIncidentBanner(input) {
|
|
1094
|
+
if (!input || typeof input !== "object") return null;
|
|
1095
|
+
const raw = input;
|
|
1096
|
+
const sev = raw.severity;
|
|
1097
|
+
if (sev !== "info" && sev !== "warning" && sev !== "outage") return null;
|
|
1098
|
+
if (typeof raw.title !== "string" || raw.title.length === 0) return null;
|
|
1099
|
+
if (typeof raw.expiresAt === "string") {
|
|
1100
|
+
const t = Date.parse(raw.expiresAt);
|
|
1101
|
+
if (!Number.isNaN(t) && t <= Date.now()) return null;
|
|
1102
|
+
}
|
|
1103
|
+
const out = { severity: sev, title: raw.title };
|
|
1104
|
+
if (typeof raw.body === "string") out.body = raw.body;
|
|
1105
|
+
if (typeof raw.eta === "string") out.eta = raw.eta;
|
|
1106
|
+
if (raw.link && typeof raw.link === "object") {
|
|
1107
|
+
const link = raw.link;
|
|
1108
|
+
if (typeof link.url === "string") {
|
|
1109
|
+
out.link = { url: link.url };
|
|
1110
|
+
if (typeof link.label === "string") out.link.label = link.label;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
if (typeof raw.expiresAt === "string") out.expiresAt = raw.expiresAt;
|
|
1114
|
+
return out;
|
|
1115
|
+
}
|
|
1116
|
+
function bannerKey(banner) {
|
|
1117
|
+
if (!banner) return null;
|
|
1118
|
+
return JSON.stringify([
|
|
1119
|
+
banner.severity,
|
|
1120
|
+
banner.title,
|
|
1121
|
+
banner.body ?? "",
|
|
1122
|
+
banner.eta ?? "",
|
|
1123
|
+
banner.link?.url ?? "",
|
|
1124
|
+
banner.link?.label ?? "",
|
|
1125
|
+
banner.expiresAt ?? ""
|
|
1126
|
+
]);
|
|
1127
|
+
}
|
|
1003
1128
|
function getStorage() {
|
|
1004
1129
|
try {
|
|
1005
|
-
|
|
1130
|
+
if (typeof window !== "undefined" && window.localStorage)
|
|
1131
|
+
return window.localStorage;
|
|
1132
|
+
if (typeof globalThis !== "undefined") {
|
|
1133
|
+
const ls = globalThis.localStorage;
|
|
1134
|
+
if (ls) return ls;
|
|
1135
|
+
}
|
|
1136
|
+
return null;
|
|
1006
1137
|
} catch {
|
|
1007
1138
|
return null;
|
|
1008
1139
|
}
|
|
@@ -1044,9 +1175,42 @@ var CustomerHeroChat = class {
|
|
|
1044
1175
|
preChatSubmission: null,
|
|
1045
1176
|
consent: this.readStoredConsent(),
|
|
1046
1177
|
pendingTriggerId: null,
|
|
1047
|
-
pendingPrefill: null
|
|
1178
|
+
pendingPrefill: null,
|
|
1179
|
+
incidentBanner: null,
|
|
1180
|
+
incidentBannerDismissed: false,
|
|
1181
|
+
readOnly: false
|
|
1048
1182
|
};
|
|
1049
1183
|
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Mark the config as loaded and put the client into read-only preview
|
|
1186
|
+
* mode without hitting the API. Used by `@customerhero/react/preview` to
|
|
1187
|
+
* render the widget against a host-supplied config (the dashboard preview
|
|
1188
|
+
* pane). Public API consumers should not call this.
|
|
1189
|
+
*
|
|
1190
|
+
* Pass a config to re-resolve and update the rendered colors/size/launcher
|
|
1191
|
+
* in place. Callers should reuse the same client instance across config
|
|
1192
|
+
* changes so the open animation only fires once.
|
|
1193
|
+
*
|
|
1194
|
+
* @internal
|
|
1195
|
+
*/
|
|
1196
|
+
__seedForPreview(config, extras) {
|
|
1197
|
+
const resolved = config ? resolveConfig(config) : this.state.config;
|
|
1198
|
+
if (config) this.userConfig = config;
|
|
1199
|
+
const seededMessages = resolved.welcomeMessage ? [{ role: "bot", content: resolved.welcomeMessage }] : [];
|
|
1200
|
+
const sanitizedBanner = extras && "banner" in extras ? sanitizeIncidentBanner(extras.banner ?? null) : this.state.incidentBanner;
|
|
1201
|
+
this.setState({
|
|
1202
|
+
config: resolved,
|
|
1203
|
+
configLoaded: true,
|
|
1204
|
+
configError: null,
|
|
1205
|
+
readOnly: true,
|
|
1206
|
+
isOpen: true,
|
|
1207
|
+
messages: seededMessages,
|
|
1208
|
+
incidentBanner: sanitizedBanner,
|
|
1209
|
+
// Reset the dismissed flag so toggling the banner on in the dashboard
|
|
1210
|
+
// re-renders it after a previous preview-side dismiss.
|
|
1211
|
+
incidentBannerDismissed: false
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1050
1214
|
// ── Proactive engagement state ─────────────────────────────────────
|
|
1051
1215
|
triggersRuntime = null;
|
|
1052
1216
|
preChatFormSubmitted = false;
|
|
@@ -1123,11 +1287,18 @@ var CustomerHeroChat = class {
|
|
|
1123
1287
|
const resolved = resolveConfig(this.userConfig, fetched);
|
|
1124
1288
|
const triggers = Array.isArray(fetched.triggers) ? fetched.triggers : [];
|
|
1125
1289
|
const preChatForm = fetched.preChatForm ?? null;
|
|
1290
|
+
const incidentBanner = sanitizeIncidentBanner(fetched.incidentBanner);
|
|
1291
|
+
const prevBannerKey = bannerKey(this.state.incidentBanner);
|
|
1292
|
+
const nextBannerKey = bannerKey(incidentBanner);
|
|
1293
|
+
const dismissedFromStorage = nextBannerKey && nextBannerKey === this.readStoredBannerDismissal();
|
|
1294
|
+
const incidentBannerDismissed = nextBannerKey === prevBannerKey ? this.state.incidentBannerDismissed || !!dismissedFromStorage : !!dismissedFromStorage;
|
|
1126
1295
|
this.setState({
|
|
1127
1296
|
config: resolved,
|
|
1128
1297
|
configLoaded: true,
|
|
1129
1298
|
triggers,
|
|
1130
|
-
preChatForm
|
|
1299
|
+
preChatForm,
|
|
1300
|
+
incidentBanner,
|
|
1301
|
+
incidentBannerDismissed
|
|
1131
1302
|
});
|
|
1132
1303
|
if (resolved.stringOverrides) this.rebuildTranslator();
|
|
1133
1304
|
this.startTriggersRuntimeIfPossible();
|
|
@@ -1185,6 +1356,7 @@ var CustomerHeroChat = class {
|
|
|
1185
1356
|
}
|
|
1186
1357
|
}
|
|
1187
1358
|
async sendMessage(message, options) {
|
|
1359
|
+
if (this.state.readOnly) return;
|
|
1188
1360
|
const trimmed = message.trim();
|
|
1189
1361
|
const attachmentTokens = options?.attachmentTokens ?? [];
|
|
1190
1362
|
if (!trimmed || this.state.isLoading) return;
|
|
@@ -1552,6 +1724,34 @@ var CustomerHeroChat = class {
|
|
|
1552
1724
|
setTraits(traits) {
|
|
1553
1725
|
this.triggersRuntime?.setTraits(traits);
|
|
1554
1726
|
}
|
|
1727
|
+
/** Hide the active incident banner for this visitor. Persisted in
|
|
1728
|
+
* localStorage so a refresh keeps it dismissed; resets automatically
|
|
1729
|
+
* when the operator changes the banner content. No-op when no banner
|
|
1730
|
+
* is showing. */
|
|
1731
|
+
dismissIncidentBanner() {
|
|
1732
|
+
const key = bannerKey(this.state.incidentBanner);
|
|
1733
|
+
if (!key) return;
|
|
1734
|
+
this.writeStoredBannerDismissal(key);
|
|
1735
|
+
this.setState({ incidentBannerDismissed: true });
|
|
1736
|
+
}
|
|
1737
|
+
readStoredBannerDismissal() {
|
|
1738
|
+
try {
|
|
1739
|
+
return this.storage?.getItem(
|
|
1740
|
+
`ch_incident_dismissed_${this.userConfig.chatbotId}`
|
|
1741
|
+
) ?? null;
|
|
1742
|
+
} catch {
|
|
1743
|
+
return null;
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
writeStoredBannerDismissal(key) {
|
|
1747
|
+
try {
|
|
1748
|
+
this.storage?.setItem(
|
|
1749
|
+
`ch_incident_dismissed_${this.userConfig.chatbotId}`,
|
|
1750
|
+
key
|
|
1751
|
+
);
|
|
1752
|
+
} catch {
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1555
1755
|
/** Submit pre-chat form answers. Synthesizes a customer record server-side
|
|
1556
1756
|
* on the next sendMessage. Resumes any pending message that was deferred
|
|
1557
1757
|
* while the form was open. */
|
|
@@ -1709,6 +1909,33 @@ function pickExtension(mime) {
|
|
|
1709
1909
|
return "jpg";
|
|
1710
1910
|
}
|
|
1711
1911
|
|
|
1912
|
+
// src/theme.ts
|
|
1913
|
+
function resolveScheme(colorScheme, prefersDark) {
|
|
1914
|
+
if (colorScheme === "dark") return "dark";
|
|
1915
|
+
if (colorScheme === "light") return "light";
|
|
1916
|
+
return prefersDark ? "dark" : "light";
|
|
1917
|
+
}
|
|
1918
|
+
function effectiveColors(config, scheme) {
|
|
1919
|
+
if (scheme === "dark") {
|
|
1920
|
+
return {
|
|
1921
|
+
primary: config.primaryColorDark ?? DEFAULTS.primaryColorDark,
|
|
1922
|
+
background: config.backgroundColorDark ?? DEFAULTS.backgroundColorDark,
|
|
1923
|
+
text: config.textColorDark ?? DEFAULTS.textColorDark
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
return {
|
|
1927
|
+
primary: config.primaryColor,
|
|
1928
|
+
background: config.backgroundColor,
|
|
1929
|
+
text: config.textColor
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1932
|
+
function sizePreset(size) {
|
|
1933
|
+
return SIZE_PRESETS[size];
|
|
1934
|
+
}
|
|
1935
|
+
function panelRadius(cornerStyle) {
|
|
1936
|
+
return CORNER_RADIUS[cornerStyle];
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1712
1939
|
// src/screenshot.ts
|
|
1713
1940
|
var ScreenshotCancelled = class extends Error {
|
|
1714
1941
|
constructor(message = "Screenshot cancelled") {
|
|
@@ -1843,8 +2070,10 @@ async function canvasToBlob(canvas, quality) {
|
|
|
1843
2070
|
}
|
|
1844
2071
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1845
2072
|
0 && (module.exports = {
|
|
2073
|
+
CORNER_RADIUS,
|
|
1846
2074
|
CustomerHeroChat,
|
|
1847
2075
|
DEFAULTS,
|
|
2076
|
+
SIZE_PRESETS,
|
|
1848
2077
|
SUPPORTED_LOCALES,
|
|
1849
2078
|
ScreenshotCancelled,
|
|
1850
2079
|
ScreenshotUnavailable,
|
|
@@ -1852,9 +2081,13 @@ async function canvasToBlob(canvas, quality) {
|
|
|
1852
2081
|
captureScreenshot,
|
|
1853
2082
|
createTranslator,
|
|
1854
2083
|
detectLocale,
|
|
2084
|
+
effectiveColors,
|
|
1855
2085
|
evaluate,
|
|
1856
2086
|
isRtlLocale,
|
|
2087
|
+
panelRadius,
|
|
1857
2088
|
pickFire,
|
|
1858
2089
|
resolveLocale,
|
|
2090
|
+
resolveScheme,
|
|
2091
|
+
sizePreset,
|
|
1859
2092
|
startTriggersRuntime
|
|
1860
2093
|
});
|