@customerhero/react 2.1.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 +91 -24
- package/dist/index.js +92 -24
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -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;
|
|
1396
|
+
}
|
|
1397
|
+
if (!stickRef.current && !isFirstRender.current) {
|
|
1398
|
+
return;
|
|
1366
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
|
{
|
|
@@ -1505,12 +1551,23 @@ function ChatInput() {
|
|
|
1505
1551
|
const [dragActive, setDragActive] = (0, import_react8.useState)(false);
|
|
1506
1552
|
const [transientError, setTransientError] = (0, import_react8.useState)(null);
|
|
1507
1553
|
const fileInputRef = (0, import_react8.useRef)(null);
|
|
1554
|
+
const textInputRef = (0, import_react8.useRef)(null);
|
|
1508
1555
|
const menuRef = (0, import_react8.useRef)(null);
|
|
1509
1556
|
const menuButtonRef = (0, import_react8.useRef)(null);
|
|
1510
1557
|
const dragCounterRef = (0, import_react8.useRef)(0);
|
|
1511
1558
|
(0, import_react8.useEffect)(() => {
|
|
1512
1559
|
setCaptureSupported((0, import_js2.canCaptureScreenshot)());
|
|
1513
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]);
|
|
1514
1571
|
(0, import_react8.useEffect)(() => {
|
|
1515
1572
|
if (pendingPrefill === null) return;
|
|
1516
1573
|
const text = consumePendingPrefill();
|
|
@@ -1688,19 +1745,27 @@ function ChatInput() {
|
|
|
1688
1745
|
};
|
|
1689
1746
|
const rowStyle = {
|
|
1690
1747
|
display: "flex",
|
|
1691
|
-
|
|
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",
|
|
1692
1751
|
gap: 8
|
|
1693
1752
|
};
|
|
1753
|
+
const TEXTAREA_MAX_HEIGHT = 140;
|
|
1694
1754
|
const inputStyle = {
|
|
1695
1755
|
flex: 1,
|
|
1696
1756
|
border: "1px solid #e0e0e0",
|
|
1697
|
-
borderRadius:
|
|
1757
|
+
borderRadius: 18,
|
|
1698
1758
|
padding: "10px 16px",
|
|
1699
1759
|
fontSize: 14,
|
|
1760
|
+
lineHeight: 1.4,
|
|
1700
1761
|
outline: "none",
|
|
1701
1762
|
background: "#fafafa",
|
|
1702
1763
|
color: config.textColor,
|
|
1703
|
-
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"
|
|
1704
1769
|
};
|
|
1705
1770
|
const sendButtonStyle = {
|
|
1706
1771
|
width: 36,
|
|
@@ -1758,6 +1823,7 @@ function ChatInput() {
|
|
|
1758
1823
|
fontSize: 14,
|
|
1759
1824
|
color: "#333",
|
|
1760
1825
|
textAlign: "left",
|
|
1826
|
+
whiteSpace: "nowrap",
|
|
1761
1827
|
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
1762
1828
|
};
|
|
1763
1829
|
const dropOverlayStyle = {
|
|
@@ -1884,9 +1950,10 @@ function ChatInput() {
|
|
|
1884
1950
|
}
|
|
1885
1951
|
),
|
|
1886
1952
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1887
|
-
"
|
|
1953
|
+
"textarea",
|
|
1888
1954
|
{
|
|
1889
|
-
|
|
1955
|
+
ref: textInputRef,
|
|
1956
|
+
rows: 1,
|
|
1890
1957
|
value,
|
|
1891
1958
|
onChange: (e) => setValue(e.target.value),
|
|
1892
1959
|
onKeyDown: handleKeyDown,
|
package/dist/index.js
CHANGED
|
@@ -711,9 +711,11 @@ function renderMarkdown(source, opts = {}) {
|
|
|
711
711
|
style: {
|
|
712
712
|
margin: "0 0 8px",
|
|
713
713
|
paddingLeft: 20,
|
|
714
|
-
lineHeight: 1.5
|
|
714
|
+
lineHeight: 1.5,
|
|
715
|
+
listStyleType: "disc",
|
|
716
|
+
listStylePosition: "outside"
|
|
715
717
|
},
|
|
716
|
-
children: block.lines.map((l, i) => /* @__PURE__ */ jsx4("li", { children: renderInline(l) }, i))
|
|
718
|
+
children: block.lines.map((l, i) => /* @__PURE__ */ jsx4("li", { style: { display: "list-item" }, children: renderInline(l) }, i))
|
|
717
719
|
},
|
|
718
720
|
key
|
|
719
721
|
);
|
|
@@ -724,9 +726,11 @@ function renderMarkdown(source, opts = {}) {
|
|
|
724
726
|
style: {
|
|
725
727
|
margin: "0 0 8px",
|
|
726
728
|
paddingLeft: 22,
|
|
727
|
-
lineHeight: 1.5
|
|
729
|
+
lineHeight: 1.5,
|
|
730
|
+
listStyleType: "decimal",
|
|
731
|
+
listStylePosition: "outside"
|
|
728
732
|
},
|
|
729
|
-
children: block.lines.map((l, i) => /* @__PURE__ */ jsx4("li", { children: renderInline(l) }, i))
|
|
733
|
+
children: block.lines.map((l, i) => /* @__PURE__ */ jsx4("li", { style: { display: "list-item" }, children: renderInline(l) }, i))
|
|
730
734
|
},
|
|
731
735
|
key
|
|
732
736
|
);
|
|
@@ -929,11 +933,11 @@ function MessageStatusPill({
|
|
|
929
933
|
const containerStyle = {
|
|
930
934
|
display: "flex",
|
|
931
935
|
alignItems: "center",
|
|
936
|
+
justifyContent: "flex-end",
|
|
932
937
|
gap: 4,
|
|
933
938
|
marginTop: 2,
|
|
934
939
|
fontSize: 11,
|
|
935
|
-
color: failed ? "#b91c1c" : "#888"
|
|
936
|
-
alignSelf: "flex-end"
|
|
940
|
+
color: failed ? "#b91c1c" : "#888"
|
|
937
941
|
};
|
|
938
942
|
const labelKey = status === "sending" ? "status_sending" : status === "sent" ? "status_sent" : "status_failed";
|
|
939
943
|
return /* @__PURE__ */ jsxs4("div", { style: containerStyle, "aria-live": "polite", children: [
|
|
@@ -1242,13 +1246,20 @@ function Message({
|
|
|
1242
1246
|
};
|
|
1243
1247
|
const linkColor = isUser ? "#ffffff" : config.primaryColor;
|
|
1244
1248
|
return /* @__PURE__ */ jsxs4(AnimatedMessage, { isUser, animate, reduced, children: [
|
|
1245
|
-
/* @__PURE__ */ jsx6(
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1249
|
+
/* @__PURE__ */ jsx6(
|
|
1250
|
+
"div",
|
|
1251
|
+
{
|
|
1252
|
+
style: bubbleStyle,
|
|
1253
|
+
"data-streaming-bubble": !isUser && message.streaming ? "true" : void 0,
|
|
1254
|
+
children: isUser ? message.content : /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1255
|
+
renderMarkdown(message.content, {
|
|
1256
|
+
sources: message.sources,
|
|
1257
|
+
linkColor
|
|
1258
|
+
}),
|
|
1259
|
+
message.streaming && /* @__PURE__ */ jsx6(StreamingCursor, { reduced })
|
|
1260
|
+
] })
|
|
1261
|
+
}
|
|
1262
|
+
),
|
|
1252
1263
|
isUser && message.status && /* @__PURE__ */ jsx6(MessageStatusPill, { status: message.status, t }),
|
|
1253
1264
|
!isUser && message.blocks?.map((block, i) => /* @__PURE__ */ jsx6(
|
|
1254
1265
|
BlockRenderer,
|
|
@@ -1333,16 +1344,51 @@ function ChatMessages() {
|
|
|
1333
1344
|
t
|
|
1334
1345
|
} = useChat();
|
|
1335
1346
|
const reduced = useReducedMotion();
|
|
1347
|
+
const containerRef = useRef3(null);
|
|
1336
1348
|
const messagesEndRef = useRef3(null);
|
|
1337
1349
|
const isFirstRender = useRef3(true);
|
|
1338
1350
|
const prevMessageCount = useRef3(0);
|
|
1351
|
+
const stickRef = useRef3(true);
|
|
1352
|
+
const suppressScrollRef = useRef3(false);
|
|
1353
|
+
const autoScrollTo = (top) => {
|
|
1354
|
+
const el = containerRef.current;
|
|
1355
|
+
if (!el) return;
|
|
1356
|
+
suppressScrollRef.current = true;
|
|
1357
|
+
el.scrollTop = top;
|
|
1358
|
+
requestAnimationFrame(() => {
|
|
1359
|
+
suppressScrollRef.current = false;
|
|
1360
|
+
});
|
|
1361
|
+
};
|
|
1362
|
+
const handleScroll = () => {
|
|
1363
|
+
if (suppressScrollRef.current) return;
|
|
1364
|
+
const el = containerRef.current;
|
|
1365
|
+
if (!el) return;
|
|
1366
|
+
const distFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
1367
|
+
stickRef.current = distFromBottom < 60;
|
|
1368
|
+
};
|
|
1339
1369
|
useEffect5(() => {
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1370
|
+
const el = containerRef.current;
|
|
1371
|
+
if (!el) return;
|
|
1372
|
+
const lastMsg2 = messages[messages.length - 1];
|
|
1373
|
+
if (messages.length > prevMessageCount.current && lastMsg2?.role === "user") {
|
|
1374
|
+
stickRef.current = true;
|
|
1375
|
+
}
|
|
1376
|
+
if (!stickRef.current && !isFirstRender.current) {
|
|
1377
|
+
return;
|
|
1345
1378
|
}
|
|
1379
|
+
const streamingBubble = el.querySelector(
|
|
1380
|
+
"[data-streaming-bubble='true']"
|
|
1381
|
+
);
|
|
1382
|
+
let target = el.scrollHeight - el.clientHeight;
|
|
1383
|
+
if (streamingBubble && streamingBubble.offsetHeight > el.clientHeight - 24) {
|
|
1384
|
+
const containerTop = el.getBoundingClientRect().top;
|
|
1385
|
+
const bubbleTop = streamingBubble.getBoundingClientRect().top;
|
|
1386
|
+
const TOP_GAP = 16;
|
|
1387
|
+
target = el.scrollTop + (bubbleTop - containerTop) - TOP_GAP;
|
|
1388
|
+
stickRef.current = false;
|
|
1389
|
+
}
|
|
1390
|
+
autoScrollTo(target);
|
|
1391
|
+
isFirstRender.current = false;
|
|
1346
1392
|
}, [messages, isLoading, reduced]);
|
|
1347
1393
|
const newStartIndex = isFirstRender.current ? messages.length : prevMessageCount.current;
|
|
1348
1394
|
useEffect5(() => {
|
|
@@ -1365,7 +1411,7 @@ function ChatMessages() {
|
|
|
1365
1411
|
};
|
|
1366
1412
|
const lastMsg = messages[messages.length - 1];
|
|
1367
1413
|
const waitingForFirstToken = isLoading && (lastMsg?.role !== "bot" || lastMsg.streaming !== true);
|
|
1368
|
-
return /* @__PURE__ */ jsxs4("div", { style: containerStyle, children: [
|
|
1414
|
+
return /* @__PURE__ */ jsxs4("div", { ref: containerRef, style: containerStyle, onScroll: handleScroll, children: [
|
|
1369
1415
|
messages.map((msg, i) => /* @__PURE__ */ jsx6(
|
|
1370
1416
|
Message,
|
|
1371
1417
|
{
|
|
@@ -1454,6 +1500,7 @@ function ChatSuggestions() {
|
|
|
1454
1500
|
// src/components/chat-input.tsx
|
|
1455
1501
|
import {
|
|
1456
1502
|
useEffect as useEffect6,
|
|
1503
|
+
useLayoutEffect,
|
|
1457
1504
|
useRef as useRef4,
|
|
1458
1505
|
useState as useState6
|
|
1459
1506
|
} from "react";
|
|
@@ -1492,12 +1539,23 @@ function ChatInput() {
|
|
|
1492
1539
|
const [dragActive, setDragActive] = useState6(false);
|
|
1493
1540
|
const [transientError, setTransientError] = useState6(null);
|
|
1494
1541
|
const fileInputRef = useRef4(null);
|
|
1542
|
+
const textInputRef = useRef4(null);
|
|
1495
1543
|
const menuRef = useRef4(null);
|
|
1496
1544
|
const menuButtonRef = useRef4(null);
|
|
1497
1545
|
const dragCounterRef = useRef4(0);
|
|
1498
1546
|
useEffect6(() => {
|
|
1499
1547
|
setCaptureSupported(canCaptureScreenshot());
|
|
1500
1548
|
}, []);
|
|
1549
|
+
useEffect6(() => {
|
|
1550
|
+
const id = requestAnimationFrame(() => textInputRef.current?.focus());
|
|
1551
|
+
return () => cancelAnimationFrame(id);
|
|
1552
|
+
}, []);
|
|
1553
|
+
useLayoutEffect(() => {
|
|
1554
|
+
const el = textInputRef.current;
|
|
1555
|
+
if (!el) return;
|
|
1556
|
+
el.style.height = "auto";
|
|
1557
|
+
el.style.height = `${el.scrollHeight}px`;
|
|
1558
|
+
}, [value]);
|
|
1501
1559
|
useEffect6(() => {
|
|
1502
1560
|
if (pendingPrefill === null) return;
|
|
1503
1561
|
const text = consumePendingPrefill();
|
|
@@ -1675,19 +1733,27 @@ function ChatInput() {
|
|
|
1675
1733
|
};
|
|
1676
1734
|
const rowStyle = {
|
|
1677
1735
|
display: "flex",
|
|
1678
|
-
|
|
1736
|
+
// Anchor the icon buttons to the bottom of the row so a multi-line
|
|
1737
|
+
// textarea grows upward without dragging them along.
|
|
1738
|
+
alignItems: "flex-end",
|
|
1679
1739
|
gap: 8
|
|
1680
1740
|
};
|
|
1741
|
+
const TEXTAREA_MAX_HEIGHT = 140;
|
|
1681
1742
|
const inputStyle = {
|
|
1682
1743
|
flex: 1,
|
|
1683
1744
|
border: "1px solid #e0e0e0",
|
|
1684
|
-
borderRadius:
|
|
1745
|
+
borderRadius: 18,
|
|
1685
1746
|
padding: "10px 16px",
|
|
1686
1747
|
fontSize: 14,
|
|
1748
|
+
lineHeight: 1.4,
|
|
1687
1749
|
outline: "none",
|
|
1688
1750
|
background: "#fafafa",
|
|
1689
1751
|
color: config.textColor,
|
|
1690
|
-
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
1752
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
1753
|
+
resize: "none",
|
|
1754
|
+
overflowY: "auto",
|
|
1755
|
+
maxHeight: TEXTAREA_MAX_HEIGHT,
|
|
1756
|
+
boxSizing: "border-box"
|
|
1691
1757
|
};
|
|
1692
1758
|
const sendButtonStyle = {
|
|
1693
1759
|
width: 36,
|
|
@@ -1745,6 +1811,7 @@ function ChatInput() {
|
|
|
1745
1811
|
fontSize: 14,
|
|
1746
1812
|
color: "#333",
|
|
1747
1813
|
textAlign: "left",
|
|
1814
|
+
whiteSpace: "nowrap",
|
|
1748
1815
|
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
1749
1816
|
};
|
|
1750
1817
|
const dropOverlayStyle = {
|
|
@@ -1871,9 +1938,10 @@ function ChatInput() {
|
|
|
1871
1938
|
}
|
|
1872
1939
|
),
|
|
1873
1940
|
/* @__PURE__ */ jsx8(
|
|
1874
|
-
"
|
|
1941
|
+
"textarea",
|
|
1875
1942
|
{
|
|
1876
|
-
|
|
1943
|
+
ref: textInputRef,
|
|
1944
|
+
rows: 1,
|
|
1877
1945
|
value,
|
|
1878
1946
|
onChange: (e) => setValue(e.target.value),
|
|
1879
1947
|
onKeyDown: handleKeyDown,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@customerhero/react",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.1",
|
|
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.
|
|
61
|
+
"@customerhero/js": "^2.1.1",
|
|
62
62
|
"@testing-library/react": "^16.1.0",
|
|
63
63
|
"@types/react": "^19.0.0",
|
|
64
64
|
"@types/react-dom": "^19.0.0",
|