@customerhero/react 2.1.1 → 2.2.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/dist/index.cjs CHANGED
@@ -27,7 +27,7 @@ __export(index_exports, {
27
27
  module.exports = __toCommonJS(index_exports);
28
28
 
29
29
  // src/components/chat-widget.tsx
30
- var import_react10 = require("react");
30
+ var import_react11 = require("react");
31
31
 
32
32
  // src/context.tsx
33
33
  var import_react = require("react");
@@ -118,6 +118,10 @@ function useChat() {
118
118
  consumePendingPrefill: (0, import_react2.useCallback)(
119
119
  () => client.consumePendingPrefill(),
120
120
  [client]
121
+ ),
122
+ dismissIncidentBanner: (0, import_react2.useCallback)(
123
+ () => client.dismissIncidentBanner(),
124
+ [client]
121
125
  )
122
126
  };
123
127
  }
@@ -207,7 +211,7 @@ function ChatBubble() {
207
211
  }
208
212
 
209
213
  // src/components/chat-window.tsx
210
- var import_react9 = require("react");
214
+ var import_react10 = require("react");
211
215
 
212
216
  // src/components/chat-header.tsx
213
217
  var import_react5 = require("react");
@@ -2226,8 +2230,231 @@ function Spinner2() {
2226
2230
  ] });
2227
2231
  }
2228
2232
 
2229
- // src/components/chat-window.tsx
2233
+ // src/components/incident-banner.tsx
2234
+ var import_react9 = require("react");
2230
2235
  var import_jsx_runtime9 = require("react/jsx-runtime");
2236
+ var PALETTE = {
2237
+ info: {
2238
+ bg: "#EFF6FF",
2239
+ fg: "#1E3A8A",
2240
+ fgMuted: "#3B5BA9",
2241
+ accent: "#2563EB",
2242
+ iconBg: "#DBEAFE",
2243
+ iconFg: "#2563EB"
2244
+ },
2245
+ warning: {
2246
+ bg: "#FFFBEB",
2247
+ fg: "#78350F",
2248
+ fgMuted: "#92541A",
2249
+ accent: "#B45309",
2250
+ iconBg: "#FEF3C7",
2251
+ iconFg: "#B45309"
2252
+ },
2253
+ outage: {
2254
+ bg: "#FEF2F2",
2255
+ fg: "#991B1B",
2256
+ fgMuted: "#B23A3A",
2257
+ accent: "#DC2626",
2258
+ iconBg: "#FEE2E2",
2259
+ iconFg: "#DC2626"
2260
+ }
2261
+ };
2262
+ function SeverityIcon({
2263
+ severity,
2264
+ color
2265
+ }) {
2266
+ const props = {
2267
+ width: 14,
2268
+ height: 14,
2269
+ viewBox: "0 0 24 24",
2270
+ fill: "none",
2271
+ stroke: color,
2272
+ strokeWidth: 2.25,
2273
+ strokeLinecap: "round",
2274
+ strokeLinejoin: "round",
2275
+ "aria-hidden": true
2276
+ };
2277
+ if (severity === "outage") {
2278
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { ...props, children: [
2279
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
2280
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
2281
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
2282
+ ] });
2283
+ }
2284
+ if (severity === "warning") {
2285
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { ...props, children: [
2286
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }),
2287
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
2288
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
2289
+ ] });
2290
+ }
2291
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { ...props, children: [
2292
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
2293
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
2294
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
2295
+ ] });
2296
+ }
2297
+ function IncidentBanner() {
2298
+ const { incidentBanner, incidentBannerDismissed, dismissIncidentBanner, t } = useChat();
2299
+ const [linkHover, setLinkHover] = (0, import_react9.useState)(false);
2300
+ const [closeHover, setCloseHover] = (0, import_react9.useState)(false);
2301
+ if (!incidentBanner || incidentBannerDismissed) return null;
2302
+ const palette = PALETTE[incidentBanner.severity];
2303
+ const wrap = {
2304
+ background: palette.bg,
2305
+ color: palette.fg,
2306
+ padding: "12px 14px 12px 12px",
2307
+ display: "flex",
2308
+ gap: 10,
2309
+ alignItems: "flex-start",
2310
+ fontSize: 13,
2311
+ lineHeight: 1.45,
2312
+ boxShadow: "inset 0 -1px 0 rgba(0,0,0,0.04)"
2313
+ };
2314
+ const iconBadge = {
2315
+ width: 22,
2316
+ height: 22,
2317
+ borderRadius: "50%",
2318
+ background: palette.iconBg,
2319
+ display: "flex",
2320
+ alignItems: "center",
2321
+ justifyContent: "center",
2322
+ flexShrink: 0,
2323
+ marginTop: 1
2324
+ };
2325
+ const titleStyle = {
2326
+ margin: 0,
2327
+ fontSize: 13,
2328
+ fontWeight: 600,
2329
+ letterSpacing: "-0.005em"
2330
+ };
2331
+ const bodyStyle = {
2332
+ margin: "3px 0 0",
2333
+ fontSize: 12.5,
2334
+ color: palette.fgMuted
2335
+ };
2336
+ const footerRowStyle = {
2337
+ display: "flex",
2338
+ flexWrap: "wrap",
2339
+ alignItems: "center",
2340
+ gap: 10,
2341
+ marginTop: 8
2342
+ };
2343
+ const etaStyle = {
2344
+ display: "inline-block",
2345
+ padding: "2px 8px",
2346
+ borderRadius: 4,
2347
+ fontSize: 11,
2348
+ fontWeight: 500,
2349
+ color: palette.accent,
2350
+ background: palette.iconBg,
2351
+ lineHeight: 1.4
2352
+ };
2353
+ const linkStyle = {
2354
+ display: "inline-flex",
2355
+ alignItems: "center",
2356
+ gap: 4,
2357
+ color: palette.accent,
2358
+ textDecoration: linkHover ? "underline" : "none",
2359
+ textUnderlineOffset: 3,
2360
+ fontSize: 12.5,
2361
+ fontWeight: 600
2362
+ };
2363
+ const closeButtonStyle = {
2364
+ background: closeHover ? "rgba(0,0,0,0.06)" : "transparent",
2365
+ border: "none",
2366
+ color: palette.fgMuted,
2367
+ cursor: "pointer",
2368
+ padding: 4,
2369
+ borderRadius: 6,
2370
+ flexShrink: 0,
2371
+ lineHeight: 0,
2372
+ marginTop: -2,
2373
+ marginRight: -2,
2374
+ transition: "background-color 0.12s ease"
2375
+ };
2376
+ const role = incidentBanner.severity === "outage" ? "alert" : "status";
2377
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { role, style: wrap, children: [
2378
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: iconBadge, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2379
+ SeverityIcon,
2380
+ {
2381
+ severity: incidentBanner.severity,
2382
+ color: palette.iconFg
2383
+ }
2384
+ ) }),
2385
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
2386
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: titleStyle, children: incidentBanner.title }),
2387
+ incidentBanner.body ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: bodyStyle, children: incidentBanner.body }) : null,
2388
+ incidentBanner.eta || incidentBanner.link ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: footerRowStyle, children: [
2389
+ incidentBanner.eta ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: etaStyle, children: incidentBanner.eta }) : null,
2390
+ incidentBanner.link ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2391
+ "a",
2392
+ {
2393
+ href: incidentBanner.link.url,
2394
+ target: "_blank",
2395
+ rel: "noopener noreferrer",
2396
+ style: linkStyle,
2397
+ onMouseEnter: () => setLinkHover(true),
2398
+ onMouseLeave: () => setLinkHover(false),
2399
+ children: [
2400
+ incidentBanner.link.label ?? t("incident_default_link_label"),
2401
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2402
+ "svg",
2403
+ {
2404
+ width: "12",
2405
+ height: "12",
2406
+ viewBox: "0 0 24 24",
2407
+ fill: "none",
2408
+ stroke: "currentColor",
2409
+ strokeWidth: "2.5",
2410
+ strokeLinecap: "round",
2411
+ strokeLinejoin: "round",
2412
+ "aria-hidden": "true",
2413
+ children: [
2414
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "5", y1: "12", x2: "19", y2: "12" }),
2415
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polyline", { points: "12 5 19 12 12 19" })
2416
+ ]
2417
+ }
2418
+ )
2419
+ ]
2420
+ }
2421
+ ) : null
2422
+ ] }) : null
2423
+ ] }),
2424
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2425
+ "button",
2426
+ {
2427
+ type: "button",
2428
+ onClick: dismissIncidentBanner,
2429
+ "aria-label": t("incident_dismiss"),
2430
+ style: closeButtonStyle,
2431
+ onMouseEnter: () => setCloseHover(true),
2432
+ onMouseLeave: () => setCloseHover(false),
2433
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2434
+ "svg",
2435
+ {
2436
+ width: "14",
2437
+ height: "14",
2438
+ viewBox: "0 0 24 24",
2439
+ fill: "none",
2440
+ stroke: "currentColor",
2441
+ strokeWidth: "2",
2442
+ strokeLinecap: "round",
2443
+ strokeLinejoin: "round",
2444
+ "aria-hidden": "true",
2445
+ children: [
2446
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2447
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2448
+ ]
2449
+ }
2450
+ )
2451
+ }
2452
+ )
2453
+ ] });
2454
+ }
2455
+
2456
+ // src/components/chat-window.tsx
2457
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2231
2458
  function ConfigError({ message, title }) {
2232
2459
  const errorStyle = {
2233
2460
  flex: 1,
@@ -2239,8 +2466,8 @@ function ConfigError({ message, title }) {
2239
2466
  textAlign: "center",
2240
2467
  gap: 8
2241
2468
  };
2242
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: errorStyle, children: [
2243
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2469
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: errorStyle, children: [
2470
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2244
2471
  "svg",
2245
2472
  {
2246
2473
  width: "32",
@@ -2252,14 +2479,14 @@ function ConfigError({ message, title }) {
2252
2479
  strokeLinecap: "round",
2253
2480
  strokeLinejoin: "round",
2254
2481
  children: [
2255
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
2256
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
2257
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
2482
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
2483
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
2484
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
2258
2485
  ]
2259
2486
  }
2260
2487
  ),
2261
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { fontSize: 14, fontWeight: 500, color: "#333", margin: 0 }, children: title }),
2262
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { fontSize: 12, color: "#999", margin: 0 }, children: message })
2488
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: { fontSize: 14, fontWeight: 500, color: "#333", margin: 0 }, children: title }),
2489
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: { fontSize: 12, color: "#999", margin: 0 }, children: message })
2263
2490
  ] });
2264
2491
  }
2265
2492
  function fieldKey(field) {
@@ -2286,9 +2513,9 @@ function validateField(field, value) {
2286
2513
  }
2287
2514
  function PreChatFormView() {
2288
2515
  const { preChatForm, submitPreChatForm, cancelPreChatForm, config, t } = useChat();
2289
- const [values, setValues] = (0, import_react9.useState)({});
2290
- const [errors, setErrors] = (0, import_react9.useState)({});
2291
- const [submitting, setSubmitting] = (0, import_react9.useState)(false);
2516
+ const [values, setValues] = (0, import_react10.useState)({});
2517
+ const [errors, setErrors] = (0, import_react10.useState)({});
2518
+ const [submitting, setSubmitting] = (0, import_react10.useState)(false);
2292
2519
  if (!preChatForm) return null;
2293
2520
  function setValue(key, value) {
2294
2521
  setValues((prev) => ({ ...prev, [key]: value }));
@@ -2384,15 +2611,15 @@ function PreChatFormView() {
2384
2611
  fontSize: 14,
2385
2612
  cursor: "pointer"
2386
2613
  };
2387
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2614
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2388
2615
  "form",
2389
2616
  {
2390
2617
  style: containerStyle,
2391
2618
  onSubmit: handleSubmit,
2392
2619
  "data-customerhero-prechat-form": true,
2393
2620
  children: [
2394
- preChatForm.title && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h3", { style: { fontSize: 16, fontWeight: 600, margin: 0 }, children: preChatForm.title }),
2395
- preChatForm.description && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { style: { fontSize: 13, margin: 0, opacity: 0.8 }, children: preChatForm.description }),
2621
+ preChatForm.title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { style: { fontSize: 16, fontWeight: 600, margin: 0 }, children: preChatForm.title }),
2622
+ preChatForm.description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { style: { fontSize: 13, margin: 0, opacity: 0.8 }, children: preChatForm.description }),
2396
2623
  preChatForm.fields.map((field) => {
2397
2624
  const k = fieldKey(field);
2398
2625
  const v = values[k];
@@ -2400,12 +2627,12 @@ function PreChatFormView() {
2400
2627
  const required = "required" in field ? !!field.required : false;
2401
2628
  const label = fieldLabel(field);
2402
2629
  if (field.kind === "textarea") {
2403
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: labelStyle, children: [
2404
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
2630
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { style: labelStyle, children: [
2631
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
2405
2632
  label,
2406
2633
  required && " *"
2407
2634
  ] }),
2408
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2635
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2409
2636
  "textarea",
2410
2637
  {
2411
2638
  style: { ...inputStyle, minHeight: 80, resize: "vertical" },
@@ -2414,32 +2641,32 @@ function PreChatFormView() {
2414
2641
  onChange: (e) => setValue(k, e.target.value)
2415
2642
  }
2416
2643
  ),
2417
- err && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: errorStyle, children: err })
2644
+ err && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: errorStyle, children: err })
2418
2645
  ] }, k);
2419
2646
  }
2420
2647
  if (field.kind === "select") {
2421
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: labelStyle, children: [
2422
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
2648
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { style: labelStyle, children: [
2649
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
2423
2650
  label,
2424
2651
  required && " *"
2425
2652
  ] }),
2426
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2653
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2427
2654
  "select",
2428
2655
  {
2429
2656
  style: inputStyle,
2430
2657
  value: v ?? "",
2431
2658
  onChange: (e) => setValue(k, e.target.value),
2432
2659
  children: [
2433
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "", children: "\u2014" }),
2434
- field.options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
2660
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "", children: "\u2014" }),
2661
+ field.options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
2435
2662
  ]
2436
2663
  }
2437
2664
  ),
2438
- err && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: errorStyle, children: err })
2665
+ err && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: errorStyle, children: err })
2439
2666
  ] }, k);
2440
2667
  }
2441
2668
  if (field.kind === "consent") {
2442
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2669
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2443
2670
  "label",
2444
2671
  {
2445
2672
  style: {
@@ -2449,7 +2676,7 @@ function PreChatFormView() {
2449
2676
  gap: 8
2450
2677
  },
2451
2678
  children: [
2452
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2679
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2453
2680
  "input",
2454
2681
  {
2455
2682
  type: "checkbox",
@@ -2458,11 +2685,11 @@ function PreChatFormView() {
2458
2685
  style: { marginTop: 3 }
2459
2686
  }
2460
2687
  ),
2461
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { style: { fontSize: 13, fontWeight: 400 }, children: [
2688
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { style: { fontSize: 13, fontWeight: 400 }, children: [
2462
2689
  field.label,
2463
- field.url && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2690
+ field.url && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2464
2691
  " ",
2465
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2692
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2466
2693
  "a",
2467
2694
  {
2468
2695
  href: field.url,
@@ -2474,7 +2701,7 @@ function PreChatFormView() {
2474
2701
  )
2475
2702
  ] })
2476
2703
  ] }),
2477
- err && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: errorStyle, children: err })
2704
+ err && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: errorStyle, children: err })
2478
2705
  ]
2479
2706
  },
2480
2707
  k
@@ -2482,12 +2709,12 @@ function PreChatFormView() {
2482
2709
  }
2483
2710
  const inputType = field.kind === "email" ? "email" : field.kind === "phone" ? "tel" : "text";
2484
2711
  const maxLength = field.kind === "text" ? field.maxLength : void 0;
2485
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: labelStyle, children: [
2486
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { children: [
2712
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("label", { style: labelStyle, children: [
2713
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
2487
2714
  label,
2488
2715
  required && " *"
2489
2716
  ] }),
2490
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2717
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2491
2718
  "input",
2492
2719
  {
2493
2720
  type: inputType,
@@ -2497,11 +2724,11 @@ function PreChatFormView() {
2497
2724
  onChange: (e) => setValue(k, e.target.value)
2498
2725
  }
2499
2726
  ),
2500
- err && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: errorStyle, children: err })
2727
+ err && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: errorStyle, children: err })
2501
2728
  ] }, k);
2502
2729
  }),
2503
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: buttonRowStyle, children: [
2504
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2730
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: buttonRowStyle, children: [
2731
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2505
2732
  "button",
2506
2733
  {
2507
2734
  type: "button",
@@ -2511,7 +2738,7 @@ function PreChatFormView() {
2511
2738
  children: t("action_cancel")
2512
2739
  }
2513
2740
  ),
2514
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "submit", style: submitStyle, disabled: submitting, children: preChatForm.submitLabel })
2741
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "submit", style: submitStyle, disabled: submitting, children: preChatForm.submitLabel })
2515
2742
  ] })
2516
2743
  ]
2517
2744
  }
@@ -2520,9 +2747,9 @@ function PreChatFormView() {
2520
2747
  function ChatWindow() {
2521
2748
  const { isOpen, config, configError, t, isRtl, preChatFormVisible } = useChat();
2522
2749
  const reduced = useReducedMotion();
2523
- const [visible, setVisible] = (0, import_react9.useState)(false);
2524
- const [shouldRender, setShouldRender] = (0, import_react9.useState)(false);
2525
- (0, import_react9.useEffect)(() => {
2750
+ const [visible, setVisible] = (0, import_react10.useState)(false);
2751
+ const [shouldRender, setShouldRender] = (0, import_react10.useState)(false);
2752
+ (0, import_react10.useEffect)(() => {
2526
2753
  if (isOpen) {
2527
2754
  setShouldRender(true);
2528
2755
  requestAnimationFrame(() => {
@@ -2571,17 +2798,18 @@ function ChatWindow() {
2571
2798
  textDecoration: "underline",
2572
2799
  textUnderlineOffset: 2
2573
2800
  };
2574
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style, dir: isRtl ? "rtl" : "ltr", children: [
2575
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatHeader, {}),
2576
- configError ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ConfigError, { title: t("unable_to_load"), message: configError }) : preChatFormVisible ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PreChatFormView, {}) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2577
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatMessages, {}),
2578
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatSuggestions, {}),
2579
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChatInput, {})
2801
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style, dir: isRtl ? "rtl" : "ltr", children: [
2802
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatHeader, {}),
2803
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(IncidentBanner, {}),
2804
+ configError ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ConfigError, { title: t("unable_to_load"), message: configError }) : preChatFormVisible ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PreChatFormView, {}) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2805
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatMessages, {}),
2806
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatSuggestions, {}),
2807
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatInput, {})
2580
2808
  ] }),
2581
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: poweredStyle, children: [
2809
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: poweredStyle, children: [
2582
2810
  t("powered_by"),
2583
2811
  " ",
2584
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2812
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2585
2813
  "a",
2586
2814
  {
2587
2815
  href: "https://customerhero.app",
@@ -2596,12 +2824,12 @@ function ChatWindow() {
2596
2824
  }
2597
2825
 
2598
2826
  // src/components/chat-widget.tsx
2599
- var import_jsx_runtime10 = require("react/jsx-runtime");
2827
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2600
2828
  function ChatWidgetInner({ identity }) {
2601
2829
  const client = useCustomerHeroClient();
2602
2830
  const { configLoaded, configError } = useChat();
2603
- const prevIdentityRef = (0, import_react10.useRef)(void 0);
2604
- (0, import_react10.useEffect)(() => {
2831
+ const prevIdentityRef = (0, import_react11.useRef)(void 0);
2832
+ (0, import_react11.useEffect)(() => {
2605
2833
  const key = identity ? JSON.stringify(identity) : void 0;
2606
2834
  if (key !== prevIdentityRef.current) {
2607
2835
  prevIdentityRef.current = key;
@@ -2611,13 +2839,13 @@ function ChatWidgetInner({ identity }) {
2611
2839
  }
2612
2840
  }, [identity, client]);
2613
2841
  if (!configLoaded || configError) return null;
2614
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2615
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatBubble, {}),
2616
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatWindow, {})
2842
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2843
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChatBubble, {}),
2844
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChatWindow, {})
2617
2845
  ] });
2618
2846
  }
2619
2847
  function ChatWidget({ identity, ...config }) {
2620
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CustomerHeroProvider, { ...config, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatWidgetInner, { identity }) });
2848
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CustomerHeroProvider, { ...config, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChatWidgetInner, { identity }) });
2621
2849
  }
2622
2850
  // Annotate the CommonJS export names for ESM import in node:
2623
2851
  0 && (module.exports = {
package/dist/index.d.cts CHANGED
@@ -44,6 +44,7 @@ interface UseChatReturn extends ChatState {
44
44
  cancelPreChatForm: () => void;
45
45
  fireTrigger: (triggerId: string) => void;
46
46
  consumePendingPrefill: () => string | null;
47
+ dismissIncidentBanner: () => void;
47
48
  }
48
49
  declare function useChat(): UseChatReturn;
49
50
 
package/dist/index.d.ts CHANGED
@@ -44,6 +44,7 @@ interface UseChatReturn extends ChatState {
44
44
  cancelPreChatForm: () => void;
45
45
  fireTrigger: (triggerId: string) => void;
46
46
  consumePendingPrefill: () => string | null;
47
+ dismissIncidentBanner: () => void;
47
48
  }
48
49
  declare function useChat(): UseChatReturn;
49
50
 
package/dist/index.js CHANGED
@@ -97,6 +97,10 @@ function useChat() {
97
97
  consumePendingPrefill: useCallback(
98
98
  () => client.consumePendingPrefill(),
99
99
  [client]
100
+ ),
101
+ dismissIncidentBanner: useCallback(
102
+ () => client.dismissIncidentBanner(),
103
+ [client]
100
104
  )
101
105
  };
102
106
  }
@@ -186,7 +190,7 @@ function ChatBubble() {
186
190
  }
187
191
 
188
192
  // src/components/chat-window.tsx
189
- import { useEffect as useEffect7, useState as useState7 } from "react";
193
+ import { useEffect as useEffect7, useState as useState8 } from "react";
190
194
 
191
195
  // src/components/chat-header.tsx
192
196
  import { useState as useState3, useEffect as useEffect4, useRef as useRef2 } from "react";
@@ -2214,8 +2218,231 @@ function Spinner2() {
2214
2218
  ] });
2215
2219
  }
2216
2220
 
2221
+ // src/components/incident-banner.tsx
2222
+ import { useState as useState7 } from "react";
2223
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2224
+ var PALETTE = {
2225
+ info: {
2226
+ bg: "#EFF6FF",
2227
+ fg: "#1E3A8A",
2228
+ fgMuted: "#3B5BA9",
2229
+ accent: "#2563EB",
2230
+ iconBg: "#DBEAFE",
2231
+ iconFg: "#2563EB"
2232
+ },
2233
+ warning: {
2234
+ bg: "#FFFBEB",
2235
+ fg: "#78350F",
2236
+ fgMuted: "#92541A",
2237
+ accent: "#B45309",
2238
+ iconBg: "#FEF3C7",
2239
+ iconFg: "#B45309"
2240
+ },
2241
+ outage: {
2242
+ bg: "#FEF2F2",
2243
+ fg: "#991B1B",
2244
+ fgMuted: "#B23A3A",
2245
+ accent: "#DC2626",
2246
+ iconBg: "#FEE2E2",
2247
+ iconFg: "#DC2626"
2248
+ }
2249
+ };
2250
+ function SeverityIcon({
2251
+ severity,
2252
+ color
2253
+ }) {
2254
+ const props = {
2255
+ width: 14,
2256
+ height: 14,
2257
+ viewBox: "0 0 24 24",
2258
+ fill: "none",
2259
+ stroke: color,
2260
+ strokeWidth: 2.25,
2261
+ strokeLinecap: "round",
2262
+ strokeLinejoin: "round",
2263
+ "aria-hidden": true
2264
+ };
2265
+ if (severity === "outage") {
2266
+ return /* @__PURE__ */ jsxs6("svg", { ...props, children: [
2267
+ /* @__PURE__ */ jsx9("circle", { cx: "12", cy: "12", r: "10" }),
2268
+ /* @__PURE__ */ jsx9("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
2269
+ /* @__PURE__ */ jsx9("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
2270
+ ] });
2271
+ }
2272
+ if (severity === "warning") {
2273
+ return /* @__PURE__ */ jsxs6("svg", { ...props, children: [
2274
+ /* @__PURE__ */ jsx9("path", { d: "M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }),
2275
+ /* @__PURE__ */ jsx9("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
2276
+ /* @__PURE__ */ jsx9("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
2277
+ ] });
2278
+ }
2279
+ return /* @__PURE__ */ jsxs6("svg", { ...props, children: [
2280
+ /* @__PURE__ */ jsx9("circle", { cx: "12", cy: "12", r: "10" }),
2281
+ /* @__PURE__ */ jsx9("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
2282
+ /* @__PURE__ */ jsx9("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
2283
+ ] });
2284
+ }
2285
+ function IncidentBanner() {
2286
+ const { incidentBanner, incidentBannerDismissed, dismissIncidentBanner, t } = useChat();
2287
+ const [linkHover, setLinkHover] = useState7(false);
2288
+ const [closeHover, setCloseHover] = useState7(false);
2289
+ if (!incidentBanner || incidentBannerDismissed) return null;
2290
+ const palette = PALETTE[incidentBanner.severity];
2291
+ const wrap = {
2292
+ background: palette.bg,
2293
+ color: palette.fg,
2294
+ padding: "12px 14px 12px 12px",
2295
+ display: "flex",
2296
+ gap: 10,
2297
+ alignItems: "flex-start",
2298
+ fontSize: 13,
2299
+ lineHeight: 1.45,
2300
+ boxShadow: "inset 0 -1px 0 rgba(0,0,0,0.04)"
2301
+ };
2302
+ const iconBadge = {
2303
+ width: 22,
2304
+ height: 22,
2305
+ borderRadius: "50%",
2306
+ background: palette.iconBg,
2307
+ display: "flex",
2308
+ alignItems: "center",
2309
+ justifyContent: "center",
2310
+ flexShrink: 0,
2311
+ marginTop: 1
2312
+ };
2313
+ const titleStyle = {
2314
+ margin: 0,
2315
+ fontSize: 13,
2316
+ fontWeight: 600,
2317
+ letterSpacing: "-0.005em"
2318
+ };
2319
+ const bodyStyle = {
2320
+ margin: "3px 0 0",
2321
+ fontSize: 12.5,
2322
+ color: palette.fgMuted
2323
+ };
2324
+ const footerRowStyle = {
2325
+ display: "flex",
2326
+ flexWrap: "wrap",
2327
+ alignItems: "center",
2328
+ gap: 10,
2329
+ marginTop: 8
2330
+ };
2331
+ const etaStyle = {
2332
+ display: "inline-block",
2333
+ padding: "2px 8px",
2334
+ borderRadius: 4,
2335
+ fontSize: 11,
2336
+ fontWeight: 500,
2337
+ color: palette.accent,
2338
+ background: palette.iconBg,
2339
+ lineHeight: 1.4
2340
+ };
2341
+ const linkStyle = {
2342
+ display: "inline-flex",
2343
+ alignItems: "center",
2344
+ gap: 4,
2345
+ color: palette.accent,
2346
+ textDecoration: linkHover ? "underline" : "none",
2347
+ textUnderlineOffset: 3,
2348
+ fontSize: 12.5,
2349
+ fontWeight: 600
2350
+ };
2351
+ const closeButtonStyle = {
2352
+ background: closeHover ? "rgba(0,0,0,0.06)" : "transparent",
2353
+ border: "none",
2354
+ color: palette.fgMuted,
2355
+ cursor: "pointer",
2356
+ padding: 4,
2357
+ borderRadius: 6,
2358
+ flexShrink: 0,
2359
+ lineHeight: 0,
2360
+ marginTop: -2,
2361
+ marginRight: -2,
2362
+ transition: "background-color 0.12s ease"
2363
+ };
2364
+ const role = incidentBanner.severity === "outage" ? "alert" : "status";
2365
+ return /* @__PURE__ */ jsxs6("div", { role, style: wrap, children: [
2366
+ /* @__PURE__ */ jsx9("div", { style: iconBadge, children: /* @__PURE__ */ jsx9(
2367
+ SeverityIcon,
2368
+ {
2369
+ severity: incidentBanner.severity,
2370
+ color: palette.iconFg
2371
+ }
2372
+ ) }),
2373
+ /* @__PURE__ */ jsxs6("div", { style: { flex: 1, minWidth: 0 }, children: [
2374
+ /* @__PURE__ */ jsx9("p", { style: titleStyle, children: incidentBanner.title }),
2375
+ incidentBanner.body ? /* @__PURE__ */ jsx9("p", { style: bodyStyle, children: incidentBanner.body }) : null,
2376
+ incidentBanner.eta || incidentBanner.link ? /* @__PURE__ */ jsxs6("div", { style: footerRowStyle, children: [
2377
+ incidentBanner.eta ? /* @__PURE__ */ jsx9("span", { style: etaStyle, children: incidentBanner.eta }) : null,
2378
+ incidentBanner.link ? /* @__PURE__ */ jsxs6(
2379
+ "a",
2380
+ {
2381
+ href: incidentBanner.link.url,
2382
+ target: "_blank",
2383
+ rel: "noopener noreferrer",
2384
+ style: linkStyle,
2385
+ onMouseEnter: () => setLinkHover(true),
2386
+ onMouseLeave: () => setLinkHover(false),
2387
+ children: [
2388
+ incidentBanner.link.label ?? t("incident_default_link_label"),
2389
+ /* @__PURE__ */ jsxs6(
2390
+ "svg",
2391
+ {
2392
+ width: "12",
2393
+ height: "12",
2394
+ viewBox: "0 0 24 24",
2395
+ fill: "none",
2396
+ stroke: "currentColor",
2397
+ strokeWidth: "2.5",
2398
+ strokeLinecap: "round",
2399
+ strokeLinejoin: "round",
2400
+ "aria-hidden": "true",
2401
+ children: [
2402
+ /* @__PURE__ */ jsx9("line", { x1: "5", y1: "12", x2: "19", y2: "12" }),
2403
+ /* @__PURE__ */ jsx9("polyline", { points: "12 5 19 12 12 19" })
2404
+ ]
2405
+ }
2406
+ )
2407
+ ]
2408
+ }
2409
+ ) : null
2410
+ ] }) : null
2411
+ ] }),
2412
+ /* @__PURE__ */ jsx9(
2413
+ "button",
2414
+ {
2415
+ type: "button",
2416
+ onClick: dismissIncidentBanner,
2417
+ "aria-label": t("incident_dismiss"),
2418
+ style: closeButtonStyle,
2419
+ onMouseEnter: () => setCloseHover(true),
2420
+ onMouseLeave: () => setCloseHover(false),
2421
+ children: /* @__PURE__ */ jsxs6(
2422
+ "svg",
2423
+ {
2424
+ width: "14",
2425
+ height: "14",
2426
+ viewBox: "0 0 24 24",
2427
+ fill: "none",
2428
+ stroke: "currentColor",
2429
+ strokeWidth: "2",
2430
+ strokeLinecap: "round",
2431
+ strokeLinejoin: "round",
2432
+ "aria-hidden": "true",
2433
+ children: [
2434
+ /* @__PURE__ */ jsx9("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2435
+ /* @__PURE__ */ jsx9("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2436
+ ]
2437
+ }
2438
+ )
2439
+ }
2440
+ )
2441
+ ] });
2442
+ }
2443
+
2217
2444
  // src/components/chat-window.tsx
2218
- import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2445
+ import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
2219
2446
  function ConfigError({ message, title }) {
2220
2447
  const errorStyle = {
2221
2448
  flex: 1,
@@ -2227,8 +2454,8 @@ function ConfigError({ message, title }) {
2227
2454
  textAlign: "center",
2228
2455
  gap: 8
2229
2456
  };
2230
- return /* @__PURE__ */ jsxs6("div", { style: errorStyle, children: [
2231
- /* @__PURE__ */ jsxs6(
2457
+ return /* @__PURE__ */ jsxs7("div", { style: errorStyle, children: [
2458
+ /* @__PURE__ */ jsxs7(
2232
2459
  "svg",
2233
2460
  {
2234
2461
  width: "32",
@@ -2240,14 +2467,14 @@ function ConfigError({ message, title }) {
2240
2467
  strokeLinecap: "round",
2241
2468
  strokeLinejoin: "round",
2242
2469
  children: [
2243
- /* @__PURE__ */ jsx9("circle", { cx: "12", cy: "12", r: "10" }),
2244
- /* @__PURE__ */ jsx9("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
2245
- /* @__PURE__ */ jsx9("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
2470
+ /* @__PURE__ */ jsx10("circle", { cx: "12", cy: "12", r: "10" }),
2471
+ /* @__PURE__ */ jsx10("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
2472
+ /* @__PURE__ */ jsx10("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
2246
2473
  ]
2247
2474
  }
2248
2475
  ),
2249
- /* @__PURE__ */ jsx9("p", { style: { fontSize: 14, fontWeight: 500, color: "#333", margin: 0 }, children: title }),
2250
- /* @__PURE__ */ jsx9("p", { style: { fontSize: 12, color: "#999", margin: 0 }, children: message })
2476
+ /* @__PURE__ */ jsx10("p", { style: { fontSize: 14, fontWeight: 500, color: "#333", margin: 0 }, children: title }),
2477
+ /* @__PURE__ */ jsx10("p", { style: { fontSize: 12, color: "#999", margin: 0 }, children: message })
2251
2478
  ] });
2252
2479
  }
2253
2480
  function fieldKey(field) {
@@ -2274,9 +2501,9 @@ function validateField(field, value) {
2274
2501
  }
2275
2502
  function PreChatFormView() {
2276
2503
  const { preChatForm, submitPreChatForm, cancelPreChatForm, config, t } = useChat();
2277
- const [values, setValues] = useState7({});
2278
- const [errors, setErrors] = useState7({});
2279
- const [submitting, setSubmitting] = useState7(false);
2504
+ const [values, setValues] = useState8({});
2505
+ const [errors, setErrors] = useState8({});
2506
+ const [submitting, setSubmitting] = useState8(false);
2280
2507
  if (!preChatForm) return null;
2281
2508
  function setValue(key, value) {
2282
2509
  setValues((prev) => ({ ...prev, [key]: value }));
@@ -2372,15 +2599,15 @@ function PreChatFormView() {
2372
2599
  fontSize: 14,
2373
2600
  cursor: "pointer"
2374
2601
  };
2375
- return /* @__PURE__ */ jsxs6(
2602
+ return /* @__PURE__ */ jsxs7(
2376
2603
  "form",
2377
2604
  {
2378
2605
  style: containerStyle,
2379
2606
  onSubmit: handleSubmit,
2380
2607
  "data-customerhero-prechat-form": true,
2381
2608
  children: [
2382
- preChatForm.title && /* @__PURE__ */ jsx9("h3", { style: { fontSize: 16, fontWeight: 600, margin: 0 }, children: preChatForm.title }),
2383
- preChatForm.description && /* @__PURE__ */ jsx9("p", { style: { fontSize: 13, margin: 0, opacity: 0.8 }, children: preChatForm.description }),
2609
+ preChatForm.title && /* @__PURE__ */ jsx10("h3", { style: { fontSize: 16, fontWeight: 600, margin: 0 }, children: preChatForm.title }),
2610
+ preChatForm.description && /* @__PURE__ */ jsx10("p", { style: { fontSize: 13, margin: 0, opacity: 0.8 }, children: preChatForm.description }),
2384
2611
  preChatForm.fields.map((field) => {
2385
2612
  const k = fieldKey(field);
2386
2613
  const v = values[k];
@@ -2388,12 +2615,12 @@ function PreChatFormView() {
2388
2615
  const required = "required" in field ? !!field.required : false;
2389
2616
  const label = fieldLabel(field);
2390
2617
  if (field.kind === "textarea") {
2391
- return /* @__PURE__ */ jsxs6("label", { style: labelStyle, children: [
2392
- /* @__PURE__ */ jsxs6("span", { children: [
2618
+ return /* @__PURE__ */ jsxs7("label", { style: labelStyle, children: [
2619
+ /* @__PURE__ */ jsxs7("span", { children: [
2393
2620
  label,
2394
2621
  required && " *"
2395
2622
  ] }),
2396
- /* @__PURE__ */ jsx9(
2623
+ /* @__PURE__ */ jsx10(
2397
2624
  "textarea",
2398
2625
  {
2399
2626
  style: { ...inputStyle, minHeight: 80, resize: "vertical" },
@@ -2402,32 +2629,32 @@ function PreChatFormView() {
2402
2629
  onChange: (e) => setValue(k, e.target.value)
2403
2630
  }
2404
2631
  ),
2405
- err && /* @__PURE__ */ jsx9("span", { style: errorStyle, children: err })
2632
+ err && /* @__PURE__ */ jsx10("span", { style: errorStyle, children: err })
2406
2633
  ] }, k);
2407
2634
  }
2408
2635
  if (field.kind === "select") {
2409
- return /* @__PURE__ */ jsxs6("label", { style: labelStyle, children: [
2410
- /* @__PURE__ */ jsxs6("span", { children: [
2636
+ return /* @__PURE__ */ jsxs7("label", { style: labelStyle, children: [
2637
+ /* @__PURE__ */ jsxs7("span", { children: [
2411
2638
  label,
2412
2639
  required && " *"
2413
2640
  ] }),
2414
- /* @__PURE__ */ jsxs6(
2641
+ /* @__PURE__ */ jsxs7(
2415
2642
  "select",
2416
2643
  {
2417
2644
  style: inputStyle,
2418
2645
  value: v ?? "",
2419
2646
  onChange: (e) => setValue(k, e.target.value),
2420
2647
  children: [
2421
- /* @__PURE__ */ jsx9("option", { value: "", children: "\u2014" }),
2422
- field.options.map((opt) => /* @__PURE__ */ jsx9("option", { value: opt.value, children: opt.label }, opt.value))
2648
+ /* @__PURE__ */ jsx10("option", { value: "", children: "\u2014" }),
2649
+ field.options.map((opt) => /* @__PURE__ */ jsx10("option", { value: opt.value, children: opt.label }, opt.value))
2423
2650
  ]
2424
2651
  }
2425
2652
  ),
2426
- err && /* @__PURE__ */ jsx9("span", { style: errorStyle, children: err })
2653
+ err && /* @__PURE__ */ jsx10("span", { style: errorStyle, children: err })
2427
2654
  ] }, k);
2428
2655
  }
2429
2656
  if (field.kind === "consent") {
2430
- return /* @__PURE__ */ jsxs6(
2657
+ return /* @__PURE__ */ jsxs7(
2431
2658
  "label",
2432
2659
  {
2433
2660
  style: {
@@ -2437,7 +2664,7 @@ function PreChatFormView() {
2437
2664
  gap: 8
2438
2665
  },
2439
2666
  children: [
2440
- /* @__PURE__ */ jsx9(
2667
+ /* @__PURE__ */ jsx10(
2441
2668
  "input",
2442
2669
  {
2443
2670
  type: "checkbox",
@@ -2446,11 +2673,11 @@ function PreChatFormView() {
2446
2673
  style: { marginTop: 3 }
2447
2674
  }
2448
2675
  ),
2449
- /* @__PURE__ */ jsxs6("span", { style: { fontSize: 13, fontWeight: 400 }, children: [
2676
+ /* @__PURE__ */ jsxs7("span", { style: { fontSize: 13, fontWeight: 400 }, children: [
2450
2677
  field.label,
2451
- field.url && /* @__PURE__ */ jsxs6(Fragment5, { children: [
2678
+ field.url && /* @__PURE__ */ jsxs7(Fragment5, { children: [
2452
2679
  " ",
2453
- /* @__PURE__ */ jsx9(
2680
+ /* @__PURE__ */ jsx10(
2454
2681
  "a",
2455
2682
  {
2456
2683
  href: field.url,
@@ -2462,7 +2689,7 @@ function PreChatFormView() {
2462
2689
  )
2463
2690
  ] })
2464
2691
  ] }),
2465
- err && /* @__PURE__ */ jsx9("span", { style: errorStyle, children: err })
2692
+ err && /* @__PURE__ */ jsx10("span", { style: errorStyle, children: err })
2466
2693
  ]
2467
2694
  },
2468
2695
  k
@@ -2470,12 +2697,12 @@ function PreChatFormView() {
2470
2697
  }
2471
2698
  const inputType = field.kind === "email" ? "email" : field.kind === "phone" ? "tel" : "text";
2472
2699
  const maxLength = field.kind === "text" ? field.maxLength : void 0;
2473
- return /* @__PURE__ */ jsxs6("label", { style: labelStyle, children: [
2474
- /* @__PURE__ */ jsxs6("span", { children: [
2700
+ return /* @__PURE__ */ jsxs7("label", { style: labelStyle, children: [
2701
+ /* @__PURE__ */ jsxs7("span", { children: [
2475
2702
  label,
2476
2703
  required && " *"
2477
2704
  ] }),
2478
- /* @__PURE__ */ jsx9(
2705
+ /* @__PURE__ */ jsx10(
2479
2706
  "input",
2480
2707
  {
2481
2708
  type: inputType,
@@ -2485,11 +2712,11 @@ function PreChatFormView() {
2485
2712
  onChange: (e) => setValue(k, e.target.value)
2486
2713
  }
2487
2714
  ),
2488
- err && /* @__PURE__ */ jsx9("span", { style: errorStyle, children: err })
2715
+ err && /* @__PURE__ */ jsx10("span", { style: errorStyle, children: err })
2489
2716
  ] }, k);
2490
2717
  }),
2491
- /* @__PURE__ */ jsxs6("div", { style: buttonRowStyle, children: [
2492
- /* @__PURE__ */ jsx9(
2718
+ /* @__PURE__ */ jsxs7("div", { style: buttonRowStyle, children: [
2719
+ /* @__PURE__ */ jsx10(
2493
2720
  "button",
2494
2721
  {
2495
2722
  type: "button",
@@ -2499,7 +2726,7 @@ function PreChatFormView() {
2499
2726
  children: t("action_cancel")
2500
2727
  }
2501
2728
  ),
2502
- /* @__PURE__ */ jsx9("button", { type: "submit", style: submitStyle, disabled: submitting, children: preChatForm.submitLabel })
2729
+ /* @__PURE__ */ jsx10("button", { type: "submit", style: submitStyle, disabled: submitting, children: preChatForm.submitLabel })
2503
2730
  ] })
2504
2731
  ]
2505
2732
  }
@@ -2508,8 +2735,8 @@ function PreChatFormView() {
2508
2735
  function ChatWindow() {
2509
2736
  const { isOpen, config, configError, t, isRtl, preChatFormVisible } = useChat();
2510
2737
  const reduced = useReducedMotion();
2511
- const [visible, setVisible] = useState7(false);
2512
- const [shouldRender, setShouldRender] = useState7(false);
2738
+ const [visible, setVisible] = useState8(false);
2739
+ const [shouldRender, setShouldRender] = useState8(false);
2513
2740
  useEffect7(() => {
2514
2741
  if (isOpen) {
2515
2742
  setShouldRender(true);
@@ -2559,17 +2786,18 @@ function ChatWindow() {
2559
2786
  textDecoration: "underline",
2560
2787
  textUnderlineOffset: 2
2561
2788
  };
2562
- return /* @__PURE__ */ jsxs6("div", { style, dir: isRtl ? "rtl" : "ltr", children: [
2563
- /* @__PURE__ */ jsx9(ChatHeader, {}),
2564
- configError ? /* @__PURE__ */ jsx9(ConfigError, { title: t("unable_to_load"), message: configError }) : preChatFormVisible ? /* @__PURE__ */ jsx9(PreChatFormView, {}) : /* @__PURE__ */ jsxs6(Fragment5, { children: [
2565
- /* @__PURE__ */ jsx9(ChatMessages, {}),
2566
- /* @__PURE__ */ jsx9(ChatSuggestions, {}),
2567
- /* @__PURE__ */ jsx9(ChatInput, {})
2789
+ return /* @__PURE__ */ jsxs7("div", { style, dir: isRtl ? "rtl" : "ltr", children: [
2790
+ /* @__PURE__ */ jsx10(ChatHeader, {}),
2791
+ /* @__PURE__ */ jsx10(IncidentBanner, {}),
2792
+ configError ? /* @__PURE__ */ jsx10(ConfigError, { title: t("unable_to_load"), message: configError }) : preChatFormVisible ? /* @__PURE__ */ jsx10(PreChatFormView, {}) : /* @__PURE__ */ jsxs7(Fragment5, { children: [
2793
+ /* @__PURE__ */ jsx10(ChatMessages, {}),
2794
+ /* @__PURE__ */ jsx10(ChatSuggestions, {}),
2795
+ /* @__PURE__ */ jsx10(ChatInput, {})
2568
2796
  ] }),
2569
- /* @__PURE__ */ jsxs6("div", { style: poweredStyle, children: [
2797
+ /* @__PURE__ */ jsxs7("div", { style: poweredStyle, children: [
2570
2798
  t("powered_by"),
2571
2799
  " ",
2572
- /* @__PURE__ */ jsx9(
2800
+ /* @__PURE__ */ jsx10(
2573
2801
  "a",
2574
2802
  {
2575
2803
  href: "https://customerhero.app",
@@ -2584,7 +2812,7 @@ function ChatWindow() {
2584
2812
  }
2585
2813
 
2586
2814
  // src/components/chat-widget.tsx
2587
- import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
2815
+ import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
2588
2816
  function ChatWidgetInner({ identity }) {
2589
2817
  const client = useCustomerHeroClient();
2590
2818
  const { configLoaded, configError } = useChat();
@@ -2599,13 +2827,13 @@ function ChatWidgetInner({ identity }) {
2599
2827
  }
2600
2828
  }, [identity, client]);
2601
2829
  if (!configLoaded || configError) return null;
2602
- return /* @__PURE__ */ jsxs7(Fragment6, { children: [
2603
- /* @__PURE__ */ jsx10(ChatBubble, {}),
2604
- /* @__PURE__ */ jsx10(ChatWindow, {})
2830
+ return /* @__PURE__ */ jsxs8(Fragment6, { children: [
2831
+ /* @__PURE__ */ jsx11(ChatBubble, {}),
2832
+ /* @__PURE__ */ jsx11(ChatWindow, {})
2605
2833
  ] });
2606
2834
  }
2607
2835
  function ChatWidget({ identity, ...config }) {
2608
- return /* @__PURE__ */ jsx10(CustomerHeroProvider, { ...config, children: /* @__PURE__ */ jsx10(ChatWidgetInner, { identity }) });
2836
+ return /* @__PURE__ */ jsx11(CustomerHeroProvider, { ...config, children: /* @__PURE__ */ jsx11(ChatWidgetInner, { identity }) });
2609
2837
  }
2610
2838
  export {
2611
2839
  ActionConfirmationCard,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@customerhero/react",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "private": false,
5
5
  "description": "React components for embedding the CustomerHero chat widget.",
6
6
  "keywords": [
@@ -58,7 +58,7 @@
58
58
  "react": ">=18"
59
59
  },
60
60
  "devDependencies": {
61
- "@customerhero/js": "^2.1.1",
61
+ "@customerhero/js": "^2.2.0",
62
62
  "@testing-library/react": "^16.1.0",
63
63
  "@types/react": "^19.0.0",
64
64
  "@types/react-dom": "^19.0.0",