@customerhero/react 2.0.0 → 2.1.1
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 +531 -107
- package/dist/index.js +535 -109
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -57,9 +57,6 @@ function useCustomerHeroClient() {
|
|
|
57
57
|
return client;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// src/components/chat-bubble.tsx
|
|
61
|
-
var import_react4 = require("react");
|
|
62
|
-
|
|
63
60
|
// src/use-chat.ts
|
|
64
61
|
var import_react2 = require("react");
|
|
65
62
|
function useChat() {
|
|
@@ -125,6 +122,9 @@ function useChat() {
|
|
|
125
122
|
};
|
|
126
123
|
}
|
|
127
124
|
|
|
125
|
+
// src/components/chat-bubble.tsx
|
|
126
|
+
var import_react4 = require("react");
|
|
127
|
+
|
|
128
128
|
// src/use-reduced-motion.ts
|
|
129
129
|
var import_react3 = require("react");
|
|
130
130
|
function useReducedMotion() {
|
|
@@ -732,9 +732,11 @@ function renderMarkdown(source, opts = {}) {
|
|
|
732
732
|
style: {
|
|
733
733
|
margin: "0 0 8px",
|
|
734
734
|
paddingLeft: 20,
|
|
735
|
-
lineHeight: 1.5
|
|
735
|
+
lineHeight: 1.5,
|
|
736
|
+
listStyleType: "disc",
|
|
737
|
+
listStylePosition: "outside"
|
|
736
738
|
},
|
|
737
|
-
children: block.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: renderInline(l) }, i))
|
|
739
|
+
children: block.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { style: { display: "list-item" }, children: renderInline(l) }, i))
|
|
738
740
|
},
|
|
739
741
|
key
|
|
740
742
|
);
|
|
@@ -745,9 +747,11 @@ function renderMarkdown(source, opts = {}) {
|
|
|
745
747
|
style: {
|
|
746
748
|
margin: "0 0 8px",
|
|
747
749
|
paddingLeft: 22,
|
|
748
|
-
lineHeight: 1.5
|
|
750
|
+
lineHeight: 1.5,
|
|
751
|
+
listStyleType: "decimal",
|
|
752
|
+
listStylePosition: "outside"
|
|
749
753
|
},
|
|
750
|
-
children: block.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: renderInline(l) }, i))
|
|
754
|
+
children: block.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { style: { display: "list-item" }, children: renderInline(l) }, i))
|
|
751
755
|
},
|
|
752
756
|
key
|
|
753
757
|
);
|
|
@@ -950,11 +954,11 @@ function MessageStatusPill({
|
|
|
950
954
|
const containerStyle = {
|
|
951
955
|
display: "flex",
|
|
952
956
|
alignItems: "center",
|
|
957
|
+
justifyContent: "flex-end",
|
|
953
958
|
gap: 4,
|
|
954
959
|
marginTop: 2,
|
|
955
960
|
fontSize: 11,
|
|
956
|
-
color: failed ? "#b91c1c" : "#888"
|
|
957
|
-
alignSelf: "flex-end"
|
|
961
|
+
color: failed ? "#b91c1c" : "#888"
|
|
958
962
|
};
|
|
959
963
|
const labelKey = status === "sending" ? "status_sending" : status === "sent" ? "status_sent" : "status_failed";
|
|
960
964
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: containerStyle, "aria-live": "polite", children: [
|
|
@@ -1263,13 +1267,20 @@ function Message({
|
|
|
1263
1267
|
};
|
|
1264
1268
|
const linkColor = isUser ? "#ffffff" : config.primaryColor;
|
|
1265
1269
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(AnimatedMessage, { isUser, animate, reduced, children: [
|
|
1266
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1270
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1271
|
+
"div",
|
|
1272
|
+
{
|
|
1273
|
+
style: bubbleStyle,
|
|
1274
|
+
"data-streaming-bubble": !isUser && message.streaming ? "true" : void 0,
|
|
1275
|
+
children: isUser ? message.content : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
1276
|
+
renderMarkdown(message.content, {
|
|
1277
|
+
sources: message.sources,
|
|
1278
|
+
linkColor
|
|
1279
|
+
}),
|
|
1280
|
+
message.streaming && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StreamingCursor, { reduced })
|
|
1281
|
+
] })
|
|
1282
|
+
}
|
|
1283
|
+
),
|
|
1273
1284
|
isUser && message.status && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MessageStatusPill, { status: message.status, t }),
|
|
1274
1285
|
!isUser && message.blocks?.map((block, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1275
1286
|
BlockRenderer,
|
|
@@ -1354,16 +1365,51 @@ function ChatMessages() {
|
|
|
1354
1365
|
t
|
|
1355
1366
|
} = useChat();
|
|
1356
1367
|
const reduced = useReducedMotion();
|
|
1368
|
+
const containerRef = (0, import_react7.useRef)(null);
|
|
1357
1369
|
const messagesEndRef = (0, import_react7.useRef)(null);
|
|
1358
1370
|
const isFirstRender = (0, import_react7.useRef)(true);
|
|
1359
1371
|
const prevMessageCount = (0, import_react7.useRef)(0);
|
|
1372
|
+
const stickRef = (0, import_react7.useRef)(true);
|
|
1373
|
+
const suppressScrollRef = (0, import_react7.useRef)(false);
|
|
1374
|
+
const autoScrollTo = (top) => {
|
|
1375
|
+
const el = containerRef.current;
|
|
1376
|
+
if (!el) return;
|
|
1377
|
+
suppressScrollRef.current = true;
|
|
1378
|
+
el.scrollTop = top;
|
|
1379
|
+
requestAnimationFrame(() => {
|
|
1380
|
+
suppressScrollRef.current = false;
|
|
1381
|
+
});
|
|
1382
|
+
};
|
|
1383
|
+
const handleScroll = () => {
|
|
1384
|
+
if (suppressScrollRef.current) return;
|
|
1385
|
+
const el = containerRef.current;
|
|
1386
|
+
if (!el) return;
|
|
1387
|
+
const distFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
1388
|
+
stickRef.current = distFromBottom < 60;
|
|
1389
|
+
};
|
|
1360
1390
|
(0, import_react7.useEffect)(() => {
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1391
|
+
const el = containerRef.current;
|
|
1392
|
+
if (!el) return;
|
|
1393
|
+
const lastMsg2 = messages[messages.length - 1];
|
|
1394
|
+
if (messages.length > prevMessageCount.current && lastMsg2?.role === "user") {
|
|
1395
|
+
stickRef.current = true;
|
|
1366
1396
|
}
|
|
1397
|
+
if (!stickRef.current && !isFirstRender.current) {
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
const streamingBubble = el.querySelector(
|
|
1401
|
+
"[data-streaming-bubble='true']"
|
|
1402
|
+
);
|
|
1403
|
+
let target = el.scrollHeight - el.clientHeight;
|
|
1404
|
+
if (streamingBubble && streamingBubble.offsetHeight > el.clientHeight - 24) {
|
|
1405
|
+
const containerTop = el.getBoundingClientRect().top;
|
|
1406
|
+
const bubbleTop = streamingBubble.getBoundingClientRect().top;
|
|
1407
|
+
const TOP_GAP = 16;
|
|
1408
|
+
target = el.scrollTop + (bubbleTop - containerTop) - TOP_GAP;
|
|
1409
|
+
stickRef.current = false;
|
|
1410
|
+
}
|
|
1411
|
+
autoScrollTo(target);
|
|
1412
|
+
isFirstRender.current = false;
|
|
1367
1413
|
}, [messages, isLoading, reduced]);
|
|
1368
1414
|
const newStartIndex = isFirstRender.current ? messages.length : prevMessageCount.current;
|
|
1369
1415
|
(0, import_react7.useEffect)(() => {
|
|
@@ -1386,7 +1432,7 @@ function ChatMessages() {
|
|
|
1386
1432
|
};
|
|
1387
1433
|
const lastMsg = messages[messages.length - 1];
|
|
1388
1434
|
const waitingForFirstToken = isLoading && (lastMsg?.role !== "bot" || lastMsg.streaming !== true);
|
|
1389
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: containerStyle, children: [
|
|
1435
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: containerRef, style: containerStyle, onScroll: handleScroll, children: [
|
|
1390
1436
|
messages.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1391
1437
|
Message,
|
|
1392
1438
|
{
|
|
@@ -1477,6 +1523,16 @@ var import_react8 = require("react");
|
|
|
1477
1523
|
var import_js2 = require("@customerhero/js");
|
|
1478
1524
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1479
1525
|
var MAX_ATTACHMENTS = 3;
|
|
1526
|
+
var ALLOWED_MIME_TYPES = [
|
|
1527
|
+
"image/png",
|
|
1528
|
+
"image/jpeg",
|
|
1529
|
+
"image/webp",
|
|
1530
|
+
"application/pdf"
|
|
1531
|
+
];
|
|
1532
|
+
var ACCEPT_ATTR = ALLOWED_MIME_TYPES.join(",");
|
|
1533
|
+
function isImageMime(mime) {
|
|
1534
|
+
return mime.startsWith("image/");
|
|
1535
|
+
}
|
|
1480
1536
|
function ChatInput() {
|
|
1481
1537
|
const {
|
|
1482
1538
|
sendMessage,
|
|
@@ -1491,9 +1547,27 @@ function ChatInput() {
|
|
|
1491
1547
|
const [value, setValue] = (0, import_react8.useState)("");
|
|
1492
1548
|
const [attachments, setAttachments] = (0, import_react8.useState)([]);
|
|
1493
1549
|
const [captureSupported, setCaptureSupported] = (0, import_react8.useState)(false);
|
|
1550
|
+
const [menuOpen, setMenuOpen] = (0, import_react8.useState)(false);
|
|
1551
|
+
const [dragActive, setDragActive] = (0, import_react8.useState)(false);
|
|
1552
|
+
const [transientError, setTransientError] = (0, import_react8.useState)(null);
|
|
1553
|
+
const fileInputRef = (0, import_react8.useRef)(null);
|
|
1554
|
+
const textInputRef = (0, import_react8.useRef)(null);
|
|
1555
|
+
const menuRef = (0, import_react8.useRef)(null);
|
|
1556
|
+
const menuButtonRef = (0, import_react8.useRef)(null);
|
|
1557
|
+
const dragCounterRef = (0, import_react8.useRef)(0);
|
|
1494
1558
|
(0, import_react8.useEffect)(() => {
|
|
1495
1559
|
setCaptureSupported((0, import_js2.canCaptureScreenshot)());
|
|
1496
1560
|
}, []);
|
|
1561
|
+
(0, import_react8.useEffect)(() => {
|
|
1562
|
+
const id = requestAnimationFrame(() => textInputRef.current?.focus());
|
|
1563
|
+
return () => cancelAnimationFrame(id);
|
|
1564
|
+
}, []);
|
|
1565
|
+
(0, import_react8.useLayoutEffect)(() => {
|
|
1566
|
+
const el = textInputRef.current;
|
|
1567
|
+
if (!el) return;
|
|
1568
|
+
el.style.height = "auto";
|
|
1569
|
+
el.style.height = `${el.scrollHeight}px`;
|
|
1570
|
+
}, [value]);
|
|
1497
1571
|
(0, import_react8.useEffect)(() => {
|
|
1498
1572
|
if (pendingPrefill === null) return;
|
|
1499
1573
|
const text = consumePendingPrefill();
|
|
@@ -1504,6 +1578,30 @@ function ChatInput() {
|
|
|
1504
1578
|
for (const a of attachments) URL.revokeObjectURL(a.previewUrl);
|
|
1505
1579
|
};
|
|
1506
1580
|
}, []);
|
|
1581
|
+
(0, import_react8.useEffect)(() => {
|
|
1582
|
+
if (!menuOpen) return;
|
|
1583
|
+
const onClick = (e) => {
|
|
1584
|
+
const target = e.target;
|
|
1585
|
+
if (menuRef.current?.contains(target) || menuButtonRef.current?.contains(target)) {
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
setMenuOpen(false);
|
|
1589
|
+
};
|
|
1590
|
+
const onKey = (e) => {
|
|
1591
|
+
if (e.key === "Escape") setMenuOpen(false);
|
|
1592
|
+
};
|
|
1593
|
+
document.addEventListener("mousedown", onClick);
|
|
1594
|
+
document.addEventListener("keydown", onKey);
|
|
1595
|
+
return () => {
|
|
1596
|
+
document.removeEventListener("mousedown", onClick);
|
|
1597
|
+
document.removeEventListener("keydown", onKey);
|
|
1598
|
+
};
|
|
1599
|
+
}, [menuOpen]);
|
|
1600
|
+
(0, import_react8.useEffect)(() => {
|
|
1601
|
+
if (!transientError) return;
|
|
1602
|
+
const id = window.setTimeout(() => setTransientError(null), 4e3);
|
|
1603
|
+
return () => window.clearTimeout(id);
|
|
1604
|
+
}, [transientError]);
|
|
1507
1605
|
const updateAttachment = (id, patch) => {
|
|
1508
1606
|
setAttachments(
|
|
1509
1607
|
(current) => current.map(
|
|
@@ -1511,16 +1609,16 @@ function ChatInput() {
|
|
|
1511
1609
|
)
|
|
1512
1610
|
);
|
|
1513
1611
|
};
|
|
1514
|
-
const startUpload = async (blob) => {
|
|
1612
|
+
const startUpload = async (blob, filename) => {
|
|
1515
1613
|
if (attachments.length >= MAX_ATTACHMENTS) return;
|
|
1516
1614
|
const id = `att_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
1517
1615
|
const previewUrl = URL.createObjectURL(blob);
|
|
1518
1616
|
setAttachments((current) => [
|
|
1519
1617
|
...current,
|
|
1520
|
-
{ id, status: "uploading", previewUrl, blob }
|
|
1618
|
+
{ id, status: "uploading", previewUrl, blob, filename }
|
|
1521
1619
|
]);
|
|
1522
1620
|
try {
|
|
1523
|
-
const { attachmentToken } = await uploadAttachment(blob);
|
|
1621
|
+
const { attachmentToken } = await uploadAttachment(blob, { filename });
|
|
1524
1622
|
updateAttachment(id, {
|
|
1525
1623
|
status: "ready",
|
|
1526
1624
|
token: attachmentToken
|
|
@@ -1529,7 +1627,31 @@ function ChatInput() {
|
|
|
1529
1627
|
updateAttachment(id, { status: "error" });
|
|
1530
1628
|
}
|
|
1531
1629
|
};
|
|
1630
|
+
const ingestFiles = (files) => {
|
|
1631
|
+
const remainingSlots = Math.max(0, MAX_ATTACHMENTS - attachments.length);
|
|
1632
|
+
if (remainingSlots === 0) return 0;
|
|
1633
|
+
let queued = 0;
|
|
1634
|
+
let rejectedAny = false;
|
|
1635
|
+
for (const f of Array.from(files)) {
|
|
1636
|
+
if (queued >= remainingSlots) break;
|
|
1637
|
+
const type = f.type;
|
|
1638
|
+
if (!ALLOWED_MIME_TYPES.includes(
|
|
1639
|
+
type
|
|
1640
|
+
)) {
|
|
1641
|
+
rejectedAny = true;
|
|
1642
|
+
continue;
|
|
1643
|
+
}
|
|
1644
|
+
const filename = typeof f.name === "string" && f.name.length > 0 ? f.name : void 0;
|
|
1645
|
+
void startUpload(f, filename);
|
|
1646
|
+
queued += 1;
|
|
1647
|
+
}
|
|
1648
|
+
if (rejectedAny) {
|
|
1649
|
+
setTransientError(t("attachment_unsupported_type"));
|
|
1650
|
+
}
|
|
1651
|
+
return queued;
|
|
1652
|
+
};
|
|
1532
1653
|
const handleCapture = async () => {
|
|
1654
|
+
setMenuOpen(false);
|
|
1533
1655
|
try {
|
|
1534
1656
|
const blob = await (0, import_js2.captureScreenshot)();
|
|
1535
1657
|
await startUpload(blob);
|
|
@@ -1537,6 +1659,17 @@ function ChatInput() {
|
|
|
1537
1659
|
if (e instanceof import_js2.ScreenshotCancelled) return;
|
|
1538
1660
|
}
|
|
1539
1661
|
};
|
|
1662
|
+
const handlePickFile = () => {
|
|
1663
|
+
setMenuOpen(false);
|
|
1664
|
+
fileInputRef.current?.click();
|
|
1665
|
+
};
|
|
1666
|
+
const handleFileInputChange = (e) => {
|
|
1667
|
+
const files = e.target.files;
|
|
1668
|
+
if (files && files.length > 0) {
|
|
1669
|
+
ingestFiles(files);
|
|
1670
|
+
}
|
|
1671
|
+
e.target.value = "";
|
|
1672
|
+
};
|
|
1540
1673
|
const handleRemove = (id) => {
|
|
1541
1674
|
setAttachments((current) => {
|
|
1542
1675
|
const target = current.find((a) => a.id === id);
|
|
@@ -1544,6 +1677,45 @@ function ChatInput() {
|
|
|
1544
1677
|
return current.filter((a) => a.id !== id);
|
|
1545
1678
|
});
|
|
1546
1679
|
};
|
|
1680
|
+
const handlePaste = (e) => {
|
|
1681
|
+
const items = e.clipboardData?.items;
|
|
1682
|
+
if (!items || items.length === 0) return;
|
|
1683
|
+
const blobs = [];
|
|
1684
|
+
for (const item of Array.from(items)) {
|
|
1685
|
+
if (item.kind !== "file") continue;
|
|
1686
|
+
const blob = item.getAsFile();
|
|
1687
|
+
if (blob) blobs.push(blob);
|
|
1688
|
+
}
|
|
1689
|
+
if (blobs.length === 0) return;
|
|
1690
|
+
e.preventDefault();
|
|
1691
|
+
ingestFiles(blobs);
|
|
1692
|
+
};
|
|
1693
|
+
const handleDragEnter = (e) => {
|
|
1694
|
+
if (!hasFiles(e)) return;
|
|
1695
|
+
e.preventDefault();
|
|
1696
|
+
dragCounterRef.current += 1;
|
|
1697
|
+
setDragActive(true);
|
|
1698
|
+
};
|
|
1699
|
+
const handleDragOver = (e) => {
|
|
1700
|
+
if (!hasFiles(e)) return;
|
|
1701
|
+
e.preventDefault();
|
|
1702
|
+
};
|
|
1703
|
+
const handleDragLeave = (e) => {
|
|
1704
|
+
if (!hasFiles(e)) return;
|
|
1705
|
+
e.preventDefault();
|
|
1706
|
+
dragCounterRef.current = Math.max(0, dragCounterRef.current - 1);
|
|
1707
|
+
if (dragCounterRef.current === 0) setDragActive(false);
|
|
1708
|
+
};
|
|
1709
|
+
const handleDrop = (e) => {
|
|
1710
|
+
if (!hasFiles(e)) return;
|
|
1711
|
+
e.preventDefault();
|
|
1712
|
+
dragCounterRef.current = 0;
|
|
1713
|
+
setDragActive(false);
|
|
1714
|
+
const files = e.dataTransfer?.files;
|
|
1715
|
+
if (files && files.length > 0) {
|
|
1716
|
+
ingestFiles(files);
|
|
1717
|
+
}
|
|
1718
|
+
};
|
|
1547
1719
|
const readyTokens = attachments.filter(
|
|
1548
1720
|
(a) => a.status === "ready"
|
|
1549
1721
|
).map((a) => a.token);
|
|
@@ -1564,6 +1736,7 @@ function ChatInput() {
|
|
|
1564
1736
|
}
|
|
1565
1737
|
};
|
|
1566
1738
|
const containerStyle = {
|
|
1739
|
+
position: "relative",
|
|
1567
1740
|
padding: "12px 16px",
|
|
1568
1741
|
borderTop: "1px solid #eee",
|
|
1569
1742
|
display: "flex",
|
|
@@ -1572,19 +1745,27 @@ function ChatInput() {
|
|
|
1572
1745
|
};
|
|
1573
1746
|
const rowStyle = {
|
|
1574
1747
|
display: "flex",
|
|
1575
|
-
|
|
1748
|
+
// Anchor the icon buttons to the bottom of the row so a multi-line
|
|
1749
|
+
// textarea grows upward without dragging them along.
|
|
1750
|
+
alignItems: "flex-end",
|
|
1576
1751
|
gap: 8
|
|
1577
1752
|
};
|
|
1753
|
+
const TEXTAREA_MAX_HEIGHT = 140;
|
|
1578
1754
|
const inputStyle = {
|
|
1579
1755
|
flex: 1,
|
|
1580
1756
|
border: "1px solid #e0e0e0",
|
|
1581
|
-
borderRadius:
|
|
1757
|
+
borderRadius: 18,
|
|
1582
1758
|
padding: "10px 16px",
|
|
1583
1759
|
fontSize: 14,
|
|
1760
|
+
lineHeight: 1.4,
|
|
1584
1761
|
outline: "none",
|
|
1585
1762
|
background: "#fafafa",
|
|
1586
1763
|
color: config.textColor,
|
|
1587
|
-
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
1764
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
1765
|
+
resize: "none",
|
|
1766
|
+
overflowY: "auto",
|
|
1767
|
+
maxHeight: TEXTAREA_MAX_HEIGHT,
|
|
1768
|
+
boxSizing: "border-box"
|
|
1588
1769
|
};
|
|
1589
1770
|
const sendButtonStyle = {
|
|
1590
1771
|
width: 36,
|
|
@@ -1616,99 +1797,238 @@ function ChatInput() {
|
|
|
1616
1797
|
flexShrink: 0,
|
|
1617
1798
|
padding: 0
|
|
1618
1799
|
});
|
|
1619
|
-
const
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1800
|
+
const menuStyle = {
|
|
1801
|
+
position: "absolute",
|
|
1802
|
+
bottom: "calc(100% + 4px)",
|
|
1803
|
+
left: 0,
|
|
1804
|
+
background: "white",
|
|
1805
|
+
border: "1px solid #e0e0e0",
|
|
1806
|
+
borderRadius: 8,
|
|
1807
|
+
boxShadow: "0 4px 16px rgba(0,0,0,0.12)",
|
|
1808
|
+
padding: 4,
|
|
1809
|
+
minWidth: 180,
|
|
1810
|
+
zIndex: 10,
|
|
1811
|
+
display: "flex",
|
|
1812
|
+
flexDirection: "column"
|
|
1813
|
+
};
|
|
1814
|
+
const menuItemStyle = {
|
|
1815
|
+
display: "flex",
|
|
1816
|
+
alignItems: "center",
|
|
1817
|
+
gap: 10,
|
|
1818
|
+
padding: "8px 12px",
|
|
1819
|
+
background: "transparent",
|
|
1820
|
+
border: "none",
|
|
1821
|
+
borderRadius: 4,
|
|
1822
|
+
cursor: "pointer",
|
|
1823
|
+
fontSize: 14,
|
|
1824
|
+
color: "#333",
|
|
1825
|
+
textAlign: "left",
|
|
1826
|
+
whiteSpace: "nowrap",
|
|
1827
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
1828
|
+
};
|
|
1829
|
+
const dropOverlayStyle = {
|
|
1830
|
+
position: "absolute",
|
|
1831
|
+
inset: 0,
|
|
1832
|
+
background: "rgba(255,255,255,0.92)",
|
|
1833
|
+
border: `2px dashed ${config.primaryColor}`,
|
|
1834
|
+
borderRadius: 4,
|
|
1835
|
+
display: "flex",
|
|
1836
|
+
alignItems: "center",
|
|
1837
|
+
justifyContent: "center",
|
|
1838
|
+
color: config.primaryColor,
|
|
1839
|
+
fontSize: 14,
|
|
1840
|
+
fontWeight: 500,
|
|
1841
|
+
pointerEvents: "none",
|
|
1842
|
+
zIndex: 11
|
|
1843
|
+
};
|
|
1844
|
+
const errorPillStyle = {
|
|
1845
|
+
alignSelf: "flex-start",
|
|
1846
|
+
background: "#fef2f2",
|
|
1847
|
+
color: "#b91c1c",
|
|
1848
|
+
border: "1px solid #fecaca",
|
|
1849
|
+
borderRadius: 12,
|
|
1850
|
+
padding: "4px 10px",
|
|
1851
|
+
fontSize: 12
|
|
1852
|
+
};
|
|
1853
|
+
const attachDisabled = attachments.length >= MAX_ATTACHMENTS || isLoading;
|
|
1854
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1855
|
+
"div",
|
|
1856
|
+
{
|
|
1857
|
+
style: containerStyle,
|
|
1858
|
+
onDragEnter: handleDragEnter,
|
|
1859
|
+
onDragOver: handleDragOver,
|
|
1860
|
+
onDragLeave: handleDragLeave,
|
|
1861
|
+
onDrop: handleDrop,
|
|
1862
|
+
children: [
|
|
1863
|
+
dragActive && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: dropOverlayStyle, "aria-hidden": "true", children: t("drop_files_here") }),
|
|
1864
|
+
transientError && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { role: "alert", style: errorPillStyle, children: transientError }),
|
|
1865
|
+
attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1866
|
+
"div",
|
|
1628
1867
|
{
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1868
|
+
style: { display: "flex", gap: 8, flexWrap: "wrap" },
|
|
1869
|
+
"aria-label": "Attachments",
|
|
1870
|
+
children: attachments.map((a) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1871
|
+
Thumbnail,
|
|
1872
|
+
{
|
|
1873
|
+
attachment: a,
|
|
1874
|
+
onRemove: () => handleRemove(a.id),
|
|
1875
|
+
t
|
|
1876
|
+
},
|
|
1877
|
+
a.id
|
|
1878
|
+
))
|
|
1879
|
+
}
|
|
1880
|
+
),
|
|
1881
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: rowStyle, children: [
|
|
1882
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { position: "relative" }, children: [
|
|
1883
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1884
|
+
"button",
|
|
1885
|
+
{
|
|
1886
|
+
ref: menuButtonRef,
|
|
1887
|
+
type: "button",
|
|
1888
|
+
onClick: () => setMenuOpen((o) => !o),
|
|
1889
|
+
disabled: attachDisabled,
|
|
1890
|
+
style: iconButtonStyle(attachDisabled),
|
|
1891
|
+
"aria-label": t("attach_menu_open"),
|
|
1892
|
+
"aria-haspopup": "menu",
|
|
1893
|
+
"aria-expanded": menuOpen,
|
|
1894
|
+
title: t("attach_menu_open"),
|
|
1895
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PaperclipIcon, {})
|
|
1896
|
+
}
|
|
1897
|
+
),
|
|
1898
|
+
menuOpen && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref: menuRef, role: "menu", style: menuStyle, children: [
|
|
1899
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1900
|
+
"button",
|
|
1901
|
+
{
|
|
1902
|
+
type: "button",
|
|
1903
|
+
role: "menuitem",
|
|
1904
|
+
onClick: handlePickFile,
|
|
1905
|
+
style: menuItemStyle,
|
|
1906
|
+
onMouseEnter: (e) => {
|
|
1907
|
+
e.currentTarget.style.background = "#f5f5f5";
|
|
1908
|
+
},
|
|
1909
|
+
onMouseLeave: (e) => {
|
|
1910
|
+
e.currentTarget.style.background = "transparent";
|
|
1911
|
+
},
|
|
1912
|
+
children: [
|
|
1913
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ImageIcon, {}),
|
|
1914
|
+
t("attach_photo")
|
|
1915
|
+
]
|
|
1916
|
+
}
|
|
1917
|
+
),
|
|
1918
|
+
captureSupported && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1919
|
+
"button",
|
|
1920
|
+
{
|
|
1921
|
+
type: "button",
|
|
1922
|
+
role: "menuitem",
|
|
1923
|
+
onClick: handleCapture,
|
|
1924
|
+
style: menuItemStyle,
|
|
1925
|
+
onMouseEnter: (e) => {
|
|
1926
|
+
e.currentTarget.style.background = "#f5f5f5";
|
|
1927
|
+
},
|
|
1928
|
+
onMouseLeave: (e) => {
|
|
1929
|
+
e.currentTarget.style.background = "transparent";
|
|
1930
|
+
},
|
|
1931
|
+
children: [
|
|
1932
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(CameraIcon, {}),
|
|
1933
|
+
t("screenshot_capture")
|
|
1934
|
+
]
|
|
1935
|
+
}
|
|
1936
|
+
)
|
|
1937
|
+
] })
|
|
1938
|
+
] }),
|
|
1939
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1940
|
+
"input",
|
|
1678
1941
|
{
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1942
|
+
ref: fileInputRef,
|
|
1943
|
+
type: "file",
|
|
1944
|
+
accept: ACCEPT_ATTR,
|
|
1945
|
+
multiple: true,
|
|
1946
|
+
onChange: handleFileInputChange,
|
|
1947
|
+
style: { display: "none" },
|
|
1948
|
+
"aria-hidden": "true",
|
|
1949
|
+
tabIndex: -1
|
|
1950
|
+
}
|
|
1951
|
+
),
|
|
1952
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1953
|
+
"textarea",
|
|
1954
|
+
{
|
|
1955
|
+
ref: textInputRef,
|
|
1956
|
+
rows: 1,
|
|
1957
|
+
value,
|
|
1958
|
+
onChange: (e) => setValue(e.target.value),
|
|
1959
|
+
onKeyDown: handleKeyDown,
|
|
1960
|
+
onPaste: handlePaste,
|
|
1961
|
+
placeholder: config.placeholderText,
|
|
1962
|
+
style: inputStyle,
|
|
1963
|
+
disabled: isLoading
|
|
1964
|
+
}
|
|
1965
|
+
),
|
|
1966
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1967
|
+
"button",
|
|
1968
|
+
{
|
|
1969
|
+
onClick: handleSend,
|
|
1970
|
+
disabled: isLoading || !value.trim(),
|
|
1971
|
+
style: sendButtonStyle,
|
|
1972
|
+
"aria-label": t("send_message"),
|
|
1973
|
+
onMouseEnter: (e) => {
|
|
1974
|
+
if (!reduced && !isLoading)
|
|
1975
|
+
e.currentTarget.style.transform = "scale(1.1)";
|
|
1976
|
+
},
|
|
1977
|
+
onMouseLeave: (e) => {
|
|
1978
|
+
if (!reduced) e.currentTarget.style.transform = "scale(1)";
|
|
1979
|
+
},
|
|
1980
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1981
|
+
"svg",
|
|
1982
|
+
{
|
|
1983
|
+
width: "16",
|
|
1984
|
+
height: "16",
|
|
1985
|
+
viewBox: "0 0 24 24",
|
|
1986
|
+
fill: "none",
|
|
1987
|
+
stroke: "currentColor",
|
|
1988
|
+
strokeWidth: "2",
|
|
1989
|
+
strokeLinecap: "round",
|
|
1990
|
+
strokeLinejoin: "round",
|
|
1991
|
+
children: [
|
|
1992
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
1993
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
1994
|
+
]
|
|
1995
|
+
}
|
|
1996
|
+
)
|
|
1691
1997
|
}
|
|
1692
1998
|
)
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1999
|
+
] })
|
|
2000
|
+
]
|
|
2001
|
+
}
|
|
2002
|
+
);
|
|
2003
|
+
}
|
|
2004
|
+
function hasFiles(e) {
|
|
2005
|
+
const types = e.dataTransfer?.types;
|
|
2006
|
+
if (!types) return false;
|
|
2007
|
+
for (let i = 0; i < types.length; i++) {
|
|
2008
|
+
if (types[i] === "Files") return true;
|
|
2009
|
+
}
|
|
2010
|
+
return false;
|
|
1697
2011
|
}
|
|
1698
2012
|
function Thumbnail({
|
|
1699
2013
|
attachment,
|
|
1700
2014
|
onRemove,
|
|
1701
2015
|
t
|
|
1702
2016
|
}) {
|
|
1703
|
-
const { status, previewUrl } = attachment;
|
|
2017
|
+
const { status, previewUrl, blob, filename } = attachment;
|
|
2018
|
+
const isImage = isImageMime(blob.type);
|
|
1704
2019
|
const wrap = {
|
|
1705
2020
|
position: "relative",
|
|
1706
|
-
width: 56,
|
|
2021
|
+
width: isImage ? 56 : 160,
|
|
1707
2022
|
height: 56,
|
|
1708
2023
|
borderRadius: 8,
|
|
1709
2024
|
overflow: "hidden",
|
|
1710
2025
|
border: status === "error" ? "2px solid #b91c1c" : "1px solid #e0e0e0",
|
|
1711
|
-
background: "#f5f5f5"
|
|
2026
|
+
background: "#f5f5f5",
|
|
2027
|
+
display: "flex",
|
|
2028
|
+
alignItems: "center",
|
|
2029
|
+
justifyContent: isImage ? "stretch" : "flex-start",
|
|
2030
|
+
gap: isImage ? 0 : 8,
|
|
2031
|
+
padding: isImage ? 0 : "0 8px"
|
|
1712
2032
|
};
|
|
1713
2033
|
const img = {
|
|
1714
2034
|
width: "100%",
|
|
@@ -1739,8 +2059,42 @@ function Thumbnail({
|
|
|
1739
2059
|
alignItems: "center",
|
|
1740
2060
|
justifyContent: "center"
|
|
1741
2061
|
};
|
|
2062
|
+
const docName = {
|
|
2063
|
+
fontSize: 12,
|
|
2064
|
+
fontWeight: 500,
|
|
2065
|
+
color: "#333",
|
|
2066
|
+
overflow: "hidden",
|
|
2067
|
+
textOverflow: "ellipsis",
|
|
2068
|
+
whiteSpace: "nowrap",
|
|
2069
|
+
maxWidth: 110,
|
|
2070
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
2071
|
+
};
|
|
2072
|
+
const docMeta = {
|
|
2073
|
+
fontSize: 11,
|
|
2074
|
+
color: "#888",
|
|
2075
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
2076
|
+
};
|
|
2077
|
+
const displayName = filename ?? (isImage ? "" : "Document");
|
|
2078
|
+
const sizeLabel = formatBytes(blob.size);
|
|
1742
2079
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: wrap, children: [
|
|
1743
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: previewUrl, alt: "", style: img }),
|
|
2080
|
+
isImage ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: previewUrl, alt: "", style: img }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
|
|
2081
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DocIcon, {}),
|
|
2082
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2083
|
+
"div",
|
|
2084
|
+
{
|
|
2085
|
+
style: {
|
|
2086
|
+
display: "flex",
|
|
2087
|
+
flexDirection: "column",
|
|
2088
|
+
minWidth: 0,
|
|
2089
|
+
flex: 1
|
|
2090
|
+
},
|
|
2091
|
+
children: [
|
|
2092
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: docName, title: displayName, children: displayName }),
|
|
2093
|
+
sizeLabel && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: docMeta, children: sizeLabel })
|
|
2094
|
+
]
|
|
2095
|
+
}
|
|
2096
|
+
)
|
|
2097
|
+
] }),
|
|
1744
2098
|
status === "uploading" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: overlay, "aria-label": "Uploading", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Spinner2, {}) }),
|
|
1745
2099
|
status === "error" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1746
2100
|
"div",
|
|
@@ -1763,7 +2117,45 @@ function Thumbnail({
|
|
|
1763
2117
|
)
|
|
1764
2118
|
] });
|
|
1765
2119
|
}
|
|
1766
|
-
function
|
|
2120
|
+
function PaperclipIcon() {
|
|
2121
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2122
|
+
"svg",
|
|
2123
|
+
{
|
|
2124
|
+
width: "20",
|
|
2125
|
+
height: "20",
|
|
2126
|
+
viewBox: "0 0 24 24",
|
|
2127
|
+
fill: "none",
|
|
2128
|
+
stroke: "currentColor",
|
|
2129
|
+
strokeWidth: "2",
|
|
2130
|
+
strokeLinecap: "round",
|
|
2131
|
+
strokeLinejoin: "round",
|
|
2132
|
+
"aria-hidden": "true",
|
|
2133
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" })
|
|
2134
|
+
}
|
|
2135
|
+
);
|
|
2136
|
+
}
|
|
2137
|
+
function ImageIcon() {
|
|
2138
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2139
|
+
"svg",
|
|
2140
|
+
{
|
|
2141
|
+
width: "18",
|
|
2142
|
+
height: "18",
|
|
2143
|
+
viewBox: "0 0 24 24",
|
|
2144
|
+
fill: "none",
|
|
2145
|
+
stroke: "currentColor",
|
|
2146
|
+
strokeWidth: "2",
|
|
2147
|
+
strokeLinecap: "round",
|
|
2148
|
+
strokeLinejoin: "round",
|
|
2149
|
+
"aria-hidden": "true",
|
|
2150
|
+
children: [
|
|
2151
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
2152
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
2153
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polyline", { points: "21 15 16 10 5 21" })
|
|
2154
|
+
]
|
|
2155
|
+
}
|
|
2156
|
+
);
|
|
2157
|
+
}
|
|
2158
|
+
function DocIcon() {
|
|
1767
2159
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1768
2160
|
"svg",
|
|
1769
2161
|
{
|
|
@@ -1776,6 +2168,36 @@ function CameraIcon() {
|
|
|
1776
2168
|
strokeLinecap: "round",
|
|
1777
2169
|
strokeLinejoin: "round",
|
|
1778
2170
|
"aria-hidden": "true",
|
|
2171
|
+
style: { color: "#666", flexShrink: 0 },
|
|
2172
|
+
children: [
|
|
2173
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
2174
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polyline", { points: "14 2 14 8 20 8" }),
|
|
2175
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "16", y1: "13", x2: "8", y2: "13" }),
|
|
2176
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "16", y1: "17", x2: "8", y2: "17" }),
|
|
2177
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polyline", { points: "10 9 9 9 8 9" })
|
|
2178
|
+
]
|
|
2179
|
+
}
|
|
2180
|
+
);
|
|
2181
|
+
}
|
|
2182
|
+
function formatBytes(bytes) {
|
|
2183
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return "";
|
|
2184
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
2185
|
+
if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
|
|
2186
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
2187
|
+
}
|
|
2188
|
+
function CameraIcon() {
|
|
2189
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
2190
|
+
"svg",
|
|
2191
|
+
{
|
|
2192
|
+
width: "18",
|
|
2193
|
+
height: "18",
|
|
2194
|
+
viewBox: "0 0 24 24",
|
|
2195
|
+
fill: "none",
|
|
2196
|
+
stroke: "currentColor",
|
|
2197
|
+
strokeWidth: "2",
|
|
2198
|
+
strokeLinecap: "round",
|
|
2199
|
+
strokeLinejoin: "round",
|
|
2200
|
+
"aria-hidden": "true",
|
|
1779
2201
|
children: [
|
|
1780
2202
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z" }),
|
|
1781
2203
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("circle", { cx: "12", cy: "13", r: "4" })
|
|
@@ -2177,6 +2599,7 @@ function ChatWindow() {
|
|
|
2177
2599
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2178
2600
|
function ChatWidgetInner({ identity }) {
|
|
2179
2601
|
const client = useCustomerHeroClient();
|
|
2602
|
+
const { configLoaded, configError } = useChat();
|
|
2180
2603
|
const prevIdentityRef = (0, import_react10.useRef)(void 0);
|
|
2181
2604
|
(0, import_react10.useEffect)(() => {
|
|
2182
2605
|
const key = identity ? JSON.stringify(identity) : void 0;
|
|
@@ -2187,6 +2610,7 @@ function ChatWidgetInner({ identity }) {
|
|
|
2187
2610
|
}
|
|
2188
2611
|
}
|
|
2189
2612
|
}, [identity, client]);
|
|
2613
|
+
if (!configLoaded || configError) return null;
|
|
2190
2614
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
2191
2615
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatBubble, {}),
|
|
2192
2616
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatWindow, {})
|