chatbotlite 0.5.0 → 0.5.2

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.
@@ -1014,35 +1014,50 @@ function RequestPayment(props) {
1014
1014
  var BOLT = "\u26A1";
1015
1015
  var DEFAULT_PRIMARY = "#0f172a";
1016
1016
  var DEFAULT_ON_PRIMARY = "#ffffff";
1017
+ function luminance(hex) {
1018
+ const m = hex.replace("#", "");
1019
+ const norm = m.length === 3 ? m.split("").map((c) => c + c).join("") : m;
1020
+ if (norm.length !== 6) return 0;
1021
+ const r = parseInt(norm.slice(0, 2), 16) / 255;
1022
+ const g = parseInt(norm.slice(2, 4), 16) / 255;
1023
+ const b = parseInt(norm.slice(4, 6), 16) / 255;
1024
+ const toLinear = (c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
1025
+ return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
1026
+ }
1017
1027
  var SURFACE = "#ffffff";
1018
- var SURFACE_MUTED = "#fafbfc";
1028
+ var CHAT_BG = "#f5f1eb";
1029
+ var BUBBLE_BOT = "#ffffff";
1030
+ var INPUT_BG = "#f1f3f5";
1019
1031
  var BORDER = "#e5e7eb";
1032
+ var BORDER_LIGHT = "rgba(15,23,42,0.06)";
1020
1033
  var TEXT_BODY = "#0f172a";
1021
1034
  var TEXT_MUTED = "#64748b";
1022
1035
  var TEXT_FAINT = "#94a3b8";
1023
- var FONT_STACK = `'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif`;
1036
+ var FONT_STACK = `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif`;
1024
1037
  var STYLE_TAG_ID = "chatbotlite-widget-styles";
1025
1038
  var KEYFRAMES = `
1026
1039
  @keyframes chatbotlite-pop { 0% { opacity: 0; transform: scale(0.6); } 100% { opacity: 1; transform: scale(1); } }
1027
1040
  @keyframes chatbotlite-slide { 0% { opacity: 0; transform: translateY(16px) scale(0.98); } 100% { opacity: 1; transform: translateY(0) scale(1); } }
1028
1041
  @keyframes chatbotlite-fade-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }
1029
1042
  @keyframes chatbotlite-dot { 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } 30% { transform: translateY(-4px); opacity: 1; } }
1030
- @keyframes chatbotlite-cursor { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0; } }
1043
+ @keyframes chatbotlite-cursor { 0%, 50% { opacity: 1; } 51%, 100% { opacity: 0.2; } }
1031
1044
  @keyframes chatbotlite-pulse { 0%, 100% { box-shadow: 0 12px 28px -8px rgba(15,23,42,0.32), 0 4px 8px -2px rgba(15,23,42,0.12); } 50% { box-shadow: 0 14px 32px -8px rgba(15,23,42,0.36), 0 6px 12px -2px rgba(15,23,42,0.16); } }
1032
1045
  .chatbotlite-launcher { transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1), box-shadow 180ms cubic-bezier(0.4, 0, 0.2, 1); animation: chatbotlite-pop 320ms cubic-bezier(0.34, 1.56, 0.64, 1), chatbotlite-pulse 3.6s ease-in-out 1.2s 2; }
1033
1046
  .chatbotlite-launcher:hover { transform: translateY(-2px) scale(1.04); }
1034
1047
  .chatbotlite-launcher:active { transform: translateY(0) scale(0.98); }
1035
- .chatbotlite-close { transition: background 120ms ease; }
1036
- .chatbotlite-close:hover { background: rgba(255,255,255,0.16); }
1048
+ .chatbotlite-close { transition: background 120ms ease, color 120ms ease; }
1049
+ .chatbotlite-close:hover { background: rgba(15,23,42,0.06); color: ${TEXT_BODY}; }
1037
1050
  .chatbotlite-send { transition: transform 120ms ease, opacity 120ms ease, box-shadow 120ms ease; }
1038
1051
  .chatbotlite-send:not(:disabled):hover { transform: translateY(-1px); }
1039
1052
  .chatbotlite-send:not(:disabled):active { transform: translateY(0); }
1040
- .chatbotlite-input:focus { box-shadow: 0 0 0 3px rgba(15,23,42,0.06); }
1053
+ .chatbotlite-input:focus { box-shadow: none; outline: none; }
1054
+ .chatbotlite-composer { transition: background 120ms ease, box-shadow 120ms ease; }
1055
+ .chatbotlite-composer:focus-within { background: ${SURFACE}; box-shadow: 0 0 0 1px ${BORDER}, 0 1px 2px rgba(15,23,42,0.04); }
1041
1056
  .chatbotlite-msg { animation: chatbotlite-fade-in 220ms cubic-bezier(0.4, 0, 0.2, 1); }
1042
1057
  .chatbotlite-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: ${TEXT_FAINT}; margin-right: 4px; animation: chatbotlite-dot 1.2s ease-in-out infinite; }
1043
- .chatbotlite-cursor { display: inline-block; width: 2px; height: 1em; background: currentColor; vertical-align: text-bottom; margin-left: 1px; animation: chatbotlite-cursor 1s steps(1) infinite; }
1044
- .chatbotlite-attach-btn:hover:not(:disabled), .chatbotlite-voice-btn:hover:not(:disabled) { background: ${BORDER}; }
1045
- .chatbotlite-attach-btn:active:not(:disabled), .chatbotlite-voice-btn:active:not(:disabled) { transform: scale(0.96); }
1058
+ .chatbotlite-cursor { display: inline-block; width: 0.5ch; vertical-align: text-bottom; margin-left: 1px; font-size: inherit; line-height: inherit; animation: chatbotlite-cursor 1s ease-in-out infinite; }
1059
+ .chatbotlite-icon-btn:hover:not(:disabled) { background: rgba(15,23,42,0.06) !important; opacity: 1 !important; }
1060
+ .chatbotlite-icon-btn:active:not(:disabled) { transform: scale(0.92); }
1046
1061
  .chatbotlite-dot:nth-child(2) { animation-delay: 0.15s; }
1047
1062
  .chatbotlite-dot:nth-child(3) { animation-delay: 0.3s; margin-right: 0; }
1048
1063
  .chatbotlite-brand:hover { color: ${TEXT_MUTED} !important; }
@@ -1068,7 +1083,8 @@ function ChatWidget(props) {
1068
1083
  const resolvedTitle = title ?? "Chat";
1069
1084
  const resolvedGreeting = greeting ?? "Hi! How can we help?";
1070
1085
  const primary = themeOverrides?.primary ?? DEFAULT_PRIMARY;
1071
- const onPrimary = themeOverrides?.onPrimary ?? DEFAULT_ON_PRIMARY;
1086
+ const primaryIsLight = luminance(primary) > 0.65;
1087
+ const onPrimary = themeOverrides?.onPrimary ?? (primaryIsLight ? "#0f172a" : DEFAULT_ON_PRIMARY);
1072
1088
  const attachCfg = props.attach;
1073
1089
  const attachEnabled = attachCfg?.enabled === true;
1074
1090
  const acceptAttr = attachCfg?.accept?.join(",");
@@ -1329,7 +1345,12 @@ function ChatWidget(props) {
1329
1345
  alignItems: "center",
1330
1346
  justifyContent: "center"
1331
1347
  },
1332
- children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { filter: "drop-shadow(0 1px 2px rgba(0,0,0,0.2))" }, children: BOLT })
1348
+ children: props.launcherIcon ? props.launcherIcon.startsWith("http") || props.launcherIcon.startsWith("/") ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: props.launcherIcon, alt: "", style: { width: 28, height: 28, objectFit: "contain" } }) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: { filter: "drop-shadow(0 1px 2px rgba(0,0,0,0.2))" }, children: props.launcherIcon }) : /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "26", height: "26", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
1349
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a8 8 0 0 1-13.6 5.8L3 19l1.2-4.4A8 8 0 1 1 21 12z" }),
1350
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "12", r: "1", fill: "currentColor", stroke: "none" }),
1351
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "1", fill: "currentColor", stroke: "none" }),
1352
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "15", cy: "12", r: "1", fill: "currentColor", stroke: "none" })
1353
+ ] })
1333
1354
  }
1334
1355
  ),
1335
1356
  open && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1358,17 +1379,49 @@ function ChatWidget(props) {
1358
1379
  },
1359
1380
  children: [
1360
1381
  /* @__PURE__ */ jsxRuntime.jsxs("header", { style: {
1361
- padding: "16px 18px",
1362
- background: primary,
1363
- color: onPrimary,
1382
+ padding: "14px 16px",
1383
+ background: SURFACE,
1384
+ color: TEXT_BODY,
1364
1385
  display: "flex",
1365
1386
  justifyContent: "space-between",
1366
1387
  alignItems: "center",
1367
- gap: 12
1388
+ gap: 12,
1389
+ borderBottom: `1px solid ${BORDER_LIGHT}`
1368
1390
  }, children: [
1369
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", lineHeight: 1.2, minWidth: 0 }, children: [
1370
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, fontSize: 15, letterSpacing: "-0.01em", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: resolvedTitle }),
1371
- subtitle && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, opacity: 0.7, marginTop: 2, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: subtitle })
1391
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: props.avatar ? 10 : 0, minWidth: 0 }, children: [
1392
+ props.avatar === true && /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1393
+ width: 32,
1394
+ height: 32,
1395
+ borderRadius: "50%",
1396
+ background: primary,
1397
+ color: onPrimary,
1398
+ display: "flex",
1399
+ alignItems: "center",
1400
+ justifyContent: "center",
1401
+ fontSize: 14,
1402
+ fontWeight: 600,
1403
+ flexShrink: 0,
1404
+ letterSpacing: "-0.02em"
1405
+ }, children: resolvedTitle.charAt(0).toUpperCase() }),
1406
+ typeof props.avatar === "string" && /* @__PURE__ */ jsxRuntime.jsx(
1407
+ "img",
1408
+ {
1409
+ src: props.avatar,
1410
+ alt: "",
1411
+ style: {
1412
+ width: 32,
1413
+ height: 32,
1414
+ borderRadius: "50%",
1415
+ objectFit: "cover",
1416
+ flexShrink: 0,
1417
+ border: `1px solid ${BORDER}`
1418
+ }
1419
+ }
1420
+ ),
1421
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", lineHeight: 1.2, minWidth: 0 }, children: [
1422
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, fontSize: 15, letterSpacing: "-0.01em", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", color: TEXT_BODY }, children: resolvedTitle }),
1423
+ (subtitle || sending) && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, color: TEXT_MUTED, marginTop: 2, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: subtitle ?? (sending ? "typing\u2026" : "") })
1424
+ ] })
1372
1425
  ] }),
1373
1426
  /* @__PURE__ */ jsxRuntime.jsx(
1374
1427
  "button",
@@ -1379,7 +1432,7 @@ function ChatWidget(props) {
1379
1432
  style: {
1380
1433
  background: "transparent",
1381
1434
  border: "none",
1382
- color: onPrimary,
1435
+ color: TEXT_MUTED,
1383
1436
  width: 32,
1384
1437
  height: 32,
1385
1438
  borderRadius: 10,
@@ -1406,7 +1459,7 @@ function ChatWidget(props) {
1406
1459
  display: "flex",
1407
1460
  flexDirection: "column",
1408
1461
  gap: 8,
1409
- background: SURFACE_MUTED
1462
+ background: CHAT_BG
1410
1463
  },
1411
1464
  children: [
1412
1465
  messages.map((m) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 6, alignItems: m.role === "user" ? "flex-end" : "stretch" }, children: [
@@ -1419,7 +1472,7 @@ function ChatWidget(props) {
1419
1472
  maxWidth: "82%",
1420
1473
  padding: "9px 13px",
1421
1474
  borderRadius: m.role === "user" ? "18px 18px 4px 18px" : "18px 18px 18px 4px",
1422
- background: m.role === "user" ? primary : SURFACE,
1475
+ background: m.role === "user" ? primary : BUBBLE_BOT,
1423
1476
  color: m.role === "user" ? onPrimary : TEXT_BODY,
1424
1477
  border: m.role === "user" ? "none" : `1px solid ${BORDER}`,
1425
1478
  fontSize: 14,
@@ -1431,7 +1484,15 @@ function ChatWidget(props) {
1431
1484
  },
1432
1485
  children: [
1433
1486
  m.content,
1434
- sending && m.role === "assistant" && m === messages[messages.length - 1] && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "chatbotlite-cursor", "aria-hidden": "true" })
1487
+ sending && m.role === "assistant" && m === messages[messages.length - 1] && /* @__PURE__ */ jsxRuntime.jsx(
1488
+ "span",
1489
+ {
1490
+ className: "chatbotlite-cursor",
1491
+ style: { color: primary },
1492
+ "aria-hidden": "true",
1493
+ children: "\u258D"
1494
+ }
1495
+ )
1435
1496
  ]
1436
1497
  }
1437
1498
  ),
@@ -1442,7 +1503,7 @@ function ChatWidget(props) {
1442
1503
  onPrimary,
1443
1504
  border: BORDER,
1444
1505
  surface: SURFACE,
1445
- surfaceMuted: SURFACE_MUTED,
1506
+ surfaceMuted: CHAT_BG,
1446
1507
  textBody: TEXT_BODY,
1447
1508
  textMuted: TEXT_MUTED
1448
1509
  };
@@ -1525,7 +1586,7 @@ function ChatWidget(props) {
1525
1586
  return null;
1526
1587
  })
1527
1588
  ] }, m.id)),
1528
- sending && /* @__PURE__ */ jsxRuntime.jsxs(
1589
+ sending && messages[messages.length - 1]?.content === "" && /* @__PURE__ */ jsxRuntime.jsxs(
1529
1590
  "div",
1530
1591
  {
1531
1592
  className: "chatbotlite-msg",
@@ -1533,7 +1594,7 @@ function ChatWidget(props) {
1533
1594
  alignSelf: "flex-start",
1534
1595
  padding: "12px 14px",
1535
1596
  borderRadius: "18px 18px 18px 4px",
1536
- background: SURFACE,
1597
+ background: BUBBLE_BOT,
1537
1598
  border: `1px solid ${BORDER}`,
1538
1599
  boxShadow: "0 1px 2px rgba(15,23,42,0.04)"
1539
1600
  },
@@ -1547,172 +1608,210 @@ function ChatWidget(props) {
1547
1608
  ]
1548
1609
  }
1549
1610
  ),
1550
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
1551
- display: "flex",
1552
- flexDirection: "column",
1553
- padding: 12,
1554
- gap: 8,
1611
+ files.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1612
+ padding: "8px 12px 0",
1555
1613
  background: SURFACE,
1556
- borderTop: `1px solid ${BORDER}`
1557
- }, children: [
1558
- files.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: files.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs(
1559
- "span",
1560
- {
1561
- style: {
1562
- display: "inline-flex",
1563
- alignItems: "center",
1564
- gap: 6,
1565
- padding: "4px 8px 4px 10px",
1566
- borderRadius: 8,
1567
- background: SURFACE_MUTED,
1568
- border: `1px solid ${BORDER}`,
1569
- fontSize: 12,
1570
- color: TEXT_BODY,
1571
- maxWidth: 200
1572
- },
1573
- children: [
1574
- /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: [
1575
- "\u{1F4CE} ",
1576
- f.name
1577
- ] }),
1614
+ display: "flex",
1615
+ flexWrap: "wrap",
1616
+ gap: 6
1617
+ }, children: files.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs(
1618
+ "span",
1619
+ {
1620
+ style: {
1621
+ display: "inline-flex",
1622
+ alignItems: "center",
1623
+ gap: 6,
1624
+ padding: "4px 8px 4px 10px",
1625
+ borderRadius: 999,
1626
+ background: INPUT_BG,
1627
+ fontSize: 12,
1628
+ color: TEXT_BODY,
1629
+ maxWidth: 200
1630
+ },
1631
+ children: [
1632
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: [
1633
+ "\u{1F4CE} ",
1634
+ f.name
1635
+ ] }),
1636
+ /* @__PURE__ */ jsxRuntime.jsx(
1637
+ "button",
1638
+ {
1639
+ onClick: () => removeFile(i),
1640
+ "aria-label": `Remove ${f.name}`,
1641
+ style: { background: "transparent", border: "none", cursor: "pointer", color: TEXT_MUTED, fontSize: 14, lineHeight: 1, padding: 0 },
1642
+ children: "\xD7"
1643
+ }
1644
+ )
1645
+ ]
1646
+ },
1647
+ `${f.name}-${i}`
1648
+ )) }),
1649
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1650
+ padding: "10px 12px 12px",
1651
+ background: SURFACE
1652
+ }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1653
+ "div",
1654
+ {
1655
+ className: "chatbotlite-composer",
1656
+ style: {
1657
+ display: "flex",
1658
+ alignItems: "center",
1659
+ gap: 4,
1660
+ padding: "4px 4px 4px 8px",
1661
+ background: INPUT_BG,
1662
+ borderRadius: 999
1663
+ },
1664
+ children: [
1665
+ attachEnabled && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1666
+ /* @__PURE__ */ jsxRuntime.jsx(
1667
+ "input",
1668
+ {
1669
+ ref: fileInputRef,
1670
+ type: "file",
1671
+ multiple: true,
1672
+ accept: acceptAttr,
1673
+ style: { display: "none" },
1674
+ onChange: (e) => {
1675
+ if (e.target.files) addFiles(e.target.files);
1676
+ e.target.value = "";
1677
+ }
1678
+ }
1679
+ ),
1578
1680
  /* @__PURE__ */ jsxRuntime.jsx(
1579
1681
  "button",
1580
1682
  {
1581
- onClick: () => removeFile(i),
1582
- "aria-label": `Remove ${f.name}`,
1683
+ className: "chatbotlite-icon-btn",
1684
+ onClick: () => fileInputRef.current?.click(),
1685
+ disabled: sending || files.length >= maxFiles,
1686
+ "aria-label": "Attach file",
1583
1687
  style: {
1688
+ width: 32,
1689
+ height: 32,
1690
+ borderRadius: "50%",
1584
1691
  background: "transparent",
1585
1692
  border: "none",
1586
- cursor: "pointer",
1587
- color: TEXT_MUTED,
1588
- fontSize: 14,
1693
+ cursor: sending || files.length >= maxFiles ? "default" : "pointer",
1694
+ opacity: sending || files.length >= maxFiles ? 0.35 : 0.7,
1695
+ fontSize: 18,
1589
1696
  lineHeight: 1,
1590
- padding: 0
1697
+ padding: 0,
1698
+ display: "flex",
1699
+ alignItems: "center",
1700
+ justifyContent: "center",
1701
+ flexShrink: 0,
1702
+ alignSelf: "center",
1703
+ transition: "opacity 120ms ease, background 120ms ease"
1591
1704
  },
1592
- children: "\xD7"
1705
+ children: "\u{1F4CE}"
1593
1706
  }
1594
1707
  )
1595
- ]
1596
- },
1597
- `${f.name}-${i}`
1598
- )) }),
1599
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8 }, children: [
1600
- attachEnabled && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1708
+ ] }),
1709
+ voiceEnabled && speechSupported && /* @__PURE__ */ jsxRuntime.jsx(
1710
+ "button",
1711
+ {
1712
+ className: "chatbotlite-icon-btn",
1713
+ onClick: toggleVoice,
1714
+ disabled: sending,
1715
+ "aria-label": voiceListening ? "Stop recording" : "Start voice input",
1716
+ style: {
1717
+ width: 32,
1718
+ height: 32,
1719
+ borderRadius: "50%",
1720
+ background: voiceListening ? primary : "transparent",
1721
+ color: voiceListening ? onPrimary : "inherit",
1722
+ border: "none",
1723
+ cursor: sending ? "default" : "pointer",
1724
+ opacity: sending ? 0.35 : voiceListening ? 1 : 0.7,
1725
+ fontSize: 16,
1726
+ lineHeight: 1,
1727
+ padding: 0,
1728
+ display: "flex",
1729
+ alignItems: "center",
1730
+ justifyContent: "center",
1731
+ flexShrink: 0,
1732
+ alignSelf: "center",
1733
+ transition: "opacity 120ms ease, background 120ms ease, color 120ms ease"
1734
+ },
1735
+ children: "\u{1F399}\uFE0F"
1736
+ }
1737
+ ),
1601
1738
  /* @__PURE__ */ jsxRuntime.jsx(
1602
- "input",
1739
+ "textarea",
1603
1740
  {
1604
- ref: fileInputRef,
1605
- type: "file",
1606
- multiple: true,
1607
- accept: acceptAttr,
1608
- style: { display: "none" },
1741
+ ref: inputRef,
1742
+ className: "chatbotlite-input",
1743
+ rows: 1,
1744
+ value: input,
1609
1745
  onChange: (e) => {
1610
- if (e.target.files) addFiles(e.target.files);
1611
- e.target.value = "";
1746
+ setInput(e.target.value);
1747
+ const el = e.currentTarget;
1748
+ el.style.height = "20px";
1749
+ if (el.scrollHeight > 28) {
1750
+ el.style.height = Math.min(el.scrollHeight, 100) + "px";
1751
+ }
1752
+ },
1753
+ onKeyDown: (e) => {
1754
+ if (e.key === "Enter" && !e.shiftKey) {
1755
+ e.preventDefault();
1756
+ void send();
1757
+ }
1758
+ },
1759
+ placeholder: "Message",
1760
+ disabled: sending,
1761
+ style: {
1762
+ flex: 1,
1763
+ padding: "4px 6px",
1764
+ margin: 0,
1765
+ border: "none",
1766
+ background: "transparent",
1767
+ fontSize: 14,
1768
+ fontFamily: FONT_STACK,
1769
+ color: TEXT_BODY,
1770
+ outline: "none",
1771
+ resize: "none",
1772
+ lineHeight: 1.4,
1773
+ height: 20,
1774
+ maxHeight: 100,
1775
+ boxSizing: "content-box",
1776
+ overflow: "hidden"
1612
1777
  }
1613
1778
  }
1614
1779
  ),
1615
1780
  /* @__PURE__ */ jsxRuntime.jsx(
1616
1781
  "button",
1617
1782
  {
1618
- className: "chatbotlite-attach-btn",
1619
- onClick: () => fileInputRef.current?.click(),
1620
- disabled: sending || files.length >= maxFiles,
1621
- "aria-label": "Attach file",
1783
+ className: "chatbotlite-send",
1784
+ onClick: () => void send(),
1785
+ disabled: sending || !input.trim() && files.length === 0,
1786
+ "aria-label": "Send message",
1622
1787
  style: {
1623
- width: 40,
1624
- height: 40,
1625
- borderRadius: 10,
1626
- background: SURFACE_MUTED,
1627
- border: `1px solid ${BORDER}`,
1628
- cursor: sending || files.length >= maxFiles ? "default" : "pointer",
1629
- opacity: sending || files.length >= maxFiles ? 0.4 : 1,
1630
- fontSize: 16,
1631
- transition: "background 120ms ease, transform 80ms ease"
1788
+ width: 34,
1789
+ height: 34,
1790
+ borderRadius: "50%",
1791
+ background: primary,
1792
+ color: onPrimary,
1793
+ border: "none",
1794
+ fontSize: 14,
1795
+ fontWeight: 600,
1796
+ fontFamily: FONT_STACK,
1797
+ cursor: sending || !input.trim() && files.length === 0 ? "default" : "pointer",
1798
+ opacity: sending || !input.trim() && files.length === 0 ? 0.35 : 1,
1799
+ display: "flex",
1800
+ alignItems: "center",
1801
+ justifyContent: "center",
1802
+ flexShrink: 0,
1803
+ padding: 0,
1804
+ transition: "opacity 120ms ease, transform 80ms ease"
1632
1805
  },
1633
- children: "\u{1F4CE}"
1806
+ children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1807
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
1808
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "5 12 12 5 19 12" })
1809
+ ] })
1634
1810
  }
1635
1811
  )
1636
- ] }),
1637
- voiceEnabled && speechSupported && /* @__PURE__ */ jsxRuntime.jsx(
1638
- "button",
1639
- {
1640
- className: "chatbotlite-voice-btn",
1641
- onClick: toggleVoice,
1642
- disabled: sending,
1643
- "aria-label": voiceListening ? "Stop recording" : "Start voice input",
1644
- style: {
1645
- width: 40,
1646
- height: 40,
1647
- borderRadius: 10,
1648
- background: voiceListening ? primary : SURFACE_MUTED,
1649
- color: voiceListening ? onPrimary : TEXT_BODY,
1650
- border: `1px solid ${voiceListening ? primary : BORDER}`,
1651
- cursor: sending ? "default" : "pointer",
1652
- opacity: sending ? 0.4 : 1,
1653
- fontSize: 16,
1654
- transition: "background 120ms ease, color 120ms ease, border-color 120ms ease, transform 80ms ease"
1655
- },
1656
- children: "\u{1F399}\uFE0F"
1657
- }
1658
- ),
1659
- /* @__PURE__ */ jsxRuntime.jsx(
1660
- "input",
1661
- {
1662
- ref: inputRef,
1663
- className: "chatbotlite-input",
1664
- type: "text",
1665
- value: input,
1666
- onChange: (e) => setInput(e.target.value),
1667
- onKeyDown: (e) => {
1668
- if (e.key === "Enter" && !e.shiftKey) {
1669
- e.preventDefault();
1670
- void send();
1671
- }
1672
- },
1673
- placeholder: "Type a message\u2026",
1674
- disabled: sending,
1675
- style: {
1676
- flex: 1,
1677
- padding: "10px 14px",
1678
- borderRadius: 12,
1679
- border: `1px solid ${BORDER}`,
1680
- background: SURFACE_MUTED,
1681
- fontSize: 14,
1682
- fontFamily: FONT_STACK,
1683
- color: TEXT_BODY,
1684
- outline: "none",
1685
- transition: "box-shadow 120ms ease, border-color 120ms ease"
1686
- }
1687
- }
1688
- ),
1689
- /* @__PURE__ */ jsxRuntime.jsx(
1690
- "button",
1691
- {
1692
- className: "chatbotlite-send",
1693
- onClick: () => void send(),
1694
- disabled: sending || !input.trim() && files.length === 0,
1695
- "aria-label": "Send message",
1696
- style: {
1697
- padding: "0 16px",
1698
- height: 40,
1699
- minWidth: 64,
1700
- borderRadius: 12,
1701
- background: primary,
1702
- color: onPrimary,
1703
- border: "none",
1704
- fontSize: 14,
1705
- fontWeight: 600,
1706
- fontFamily: FONT_STACK,
1707
- cursor: sending || !input.trim() && files.length === 0 ? "default" : "pointer",
1708
- opacity: sending || !input.trim() && files.length === 0 ? 0.4 : 1,
1709
- boxShadow: "0 2px 6px -1px rgba(15,23,42,0.18)"
1710
- },
1711
- children: "Send"
1712
- }
1713
- )
1714
- ] })
1715
- ] }),
1812
+ ]
1813
+ }
1814
+ ) }),
1716
1815
  showBranding && /* @__PURE__ */ jsxRuntime.jsxs(
1717
1816
  "a",
1718
1817
  {