@yassirbenmoussa/aicommerce-sdk 1.8.0 → 1.9.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
@@ -904,17 +904,21 @@ function createWidgetStyles(config) {
904
904
  }
905
905
 
906
906
  /* ============================================
907
- Embedded Mode Styles
907
+ Embedded Mode Styles - ChatGPT Style
908
908
  ============================================ */
909
909
 
910
- /* Embedded container - position relative, inline flow */
910
+ /* Embedded container - fit content with max height */
911
911
  #aicommerce-widget.aicommerce-embedded {
912
912
  position: relative;
913
913
  bottom: auto;
914
914
  left: auto;
915
915
  right: auto;
916
916
  width: 100%;
917
- height: var(--aic-height, 500px);
917
+ height: auto;
918
+ max-height: var(--aic-max-height, 600px);
919
+ display: flex;
920
+ flex-direction: column;
921
+ background: transparent;
918
922
  }
919
923
 
920
924
  /* Embedded mode: hide launcher button */
@@ -922,17 +926,30 @@ function createWidgetStyles(config) {
922
926
  display: none !important;
923
927
  }
924
928
 
925
- /* Embedded mode: chat is always visible and fills container */
929
+ /* Embedded mode: hide the header completely */
930
+ .aicommerce-embedded .aicommerce-header {
931
+ display: none !important;
932
+ }
933
+
934
+ /* Embedded mode: hide close button */
935
+ .aicommerce-embedded .aicommerce-close {
936
+ display: none !important;
937
+ }
938
+
939
+ /* Embedded mode: chat fills container with transparent background */
926
940
  .aicommerce-embedded .aicommerce-chat {
927
941
  position: relative;
928
942
  width: 100%;
929
- height: 100%;
943
+ height: auto;
930
944
  max-width: 100%;
931
- max-height: 100%;
932
- border-radius: var(--aic-radius);
945
+ border-radius: 0;
946
+ background: transparent;
947
+ box-shadow: none;
933
948
  transform: none !important;
934
949
  opacity: 1 !important;
935
950
  pointer-events: auto !important;
951
+ display: flex;
952
+ flex-direction: column;
936
953
  }
937
954
 
938
955
  /* Embedded mode: no open/close animations */
@@ -942,22 +959,244 @@ function createWidgetStyles(config) {
942
959
  pointer-events: auto !important;
943
960
  }
944
961
 
945
- /* Embedded mode: messages area adjusts to container */
962
+ /* Embedded mode: messages area - messages start from bottom, grow upward */
946
963
  .aicommerce-embedded .aicommerce-messages {
964
+ display: flex;
965
+ flex-direction: column;
966
+ justify-content: flex-end;
967
+ overflow-y: auto;
968
+ max-height: calc(var(--aic-max-height, 600px) - 100px);
969
+ padding: 16px;
970
+ max-width: 700px;
971
+ margin: 0 auto;
972
+ width: 100%;
973
+ background: transparent;
974
+ gap: 12px;
975
+ scrollbar-width: thin;
976
+ scrollbar-color: var(--aic-border) transparent;
977
+ }
978
+
979
+ /* Embedded mode: custom scrollbar for webkit browsers */
980
+ .aicommerce-embedded .aicommerce-messages::-webkit-scrollbar {
981
+ width: 6px;
982
+ }
983
+
984
+ .aicommerce-embedded .aicommerce-messages::-webkit-scrollbar-track {
985
+ background: transparent;
986
+ }
987
+
988
+ .aicommerce-embedded .aicommerce-messages::-webkit-scrollbar-thumb {
989
+ background-color: var(--aic-border);
990
+ border-radius: 3px;
991
+ }
992
+
993
+ .aicommerce-embedded .aicommerce-messages::-webkit-scrollbar-thumb:hover {
994
+ background-color: var(--aic-text-secondary);
995
+ }
996
+
997
+ /* Embedded mode: messages have different styling */
998
+ .aicommerce-embedded .aicommerce-message {
999
+ max-width: 100%;
1000
+ }
1001
+
1002
+ .aicommerce-embedded .aicommerce-message-content {
1003
+ background: var(--aic-bg);
1004
+ border: 1px solid var(--aic-border);
1005
+ border-radius: 12px;
1006
+ padding: 12px 16px;
1007
+ }
1008
+
1009
+ .aicommerce-embedded .aicommerce-user .aicommerce-message-content {
1010
+ background: var(--aic-primary);
1011
+ color: white;
1012
+ border-color: var(--aic-primary);
1013
+ }
1014
+
1015
+ /* Embedded mode: input container wrapper - always at bottom */
1016
+ .aicommerce-embedded .aicommerce-input-wrapper {
1017
+ width: 100%;
1018
+ display: flex;
1019
+ flex-direction: column;
1020
+ align-items: center;
1021
+ padding: 16px;
1022
+ flex-shrink: 0;
1023
+ }
1024
+
1025
+ /* Embedded mode: input container styled like ChatGPT */
1026
+ .aicommerce-embedded .aicommerce-input-container {
1027
+ width: 100%;
1028
+ max-width: 700px;
1029
+ background: var(--aic-bg);
1030
+ border: 1px solid var(--aic-border);
1031
+ border-radius: 24px;
1032
+ padding: 8px 12px;
1033
+ display: flex;
1034
+ align-items: flex-end;
1035
+ gap: 8px;
1036
+ }
1037
+
1038
+ /* Embedded mode: textarea field styling - auto-grow */
1039
+ .aicommerce-embedded .aicommerce-input {
947
1040
  flex: 1;
948
- min-height: 0;
1041
+ min-width: 0;
1042
+ border: none;
1043
+ background: transparent;
1044
+ padding: 8px 4px;
1045
+ font-size: 16px;
1046
+ font-family: inherit;
1047
+ color: var(--aic-text);
1048
+ outline: none;
1049
+ resize: none;
1050
+ min-height: 24px;
1051
+ max-height: 150px;
1052
+ line-height: 1.5;
1053
+ overflow-y: auto;
949
1054
  }
950
1055
 
951
- /* Embedded mode responsive */
952
- @media (max-width: 420px) {
953
- #aicommerce-widget.aicommerce-embedded {
954
- height: var(--aic-height, 400px);
1056
+ .aicommerce-embedded .aicommerce-input::placeholder {
1057
+ color: var(--aic-text-secondary);
1058
+ }
1059
+
1060
+ /* Embedded mode: buttons styling - smaller, no overflow */
1061
+ .aicommerce-embedded .aicommerce-mic,
1062
+ .aicommerce-embedded .aicommerce-send {
1063
+ flex-shrink: 0;
1064
+ width: 36px;
1065
+ height: 36px;
1066
+ min-width: 36px;
1067
+ border-radius: 50%;
1068
+ background: var(--aic-primary);
1069
+ color: white;
1070
+ border: none;
1071
+ display: flex;
1072
+ align-items: center;
1073
+ justify-content: center;
1074
+ cursor: pointer;
1075
+ transition: all 0.2s ease;
1076
+ }
1077
+
1078
+ .aicommerce-embedded .aicommerce-mic:hover,
1079
+ .aicommerce-embedded .aicommerce-send:hover {
1080
+ opacity: 0.9;
1081
+ }
1082
+
1083
+ .aicommerce-embedded .aicommerce-mic {
1084
+ background: transparent;
1085
+ color: var(--aic-text-secondary);
1086
+ }
1087
+
1088
+ .aicommerce-embedded .aicommerce-mic:hover {
1089
+ background: rgba(0, 0, 0, 0.05);
1090
+ color: var(--aic-text);
1091
+ }
1092
+
1093
+ /* Embedded mode: product cards styling */
1094
+ .aicommerce-embedded .aicommerce-products {
1095
+ display: flex;
1096
+ flex-wrap: wrap;
1097
+ gap: 12px;
1098
+ margin-top: 12px;
1099
+ }
1100
+
1101
+ .aicommerce-embedded .aicommerce-product-card {
1102
+ background: var(--aic-bg);
1103
+ border: 1px solid var(--aic-border);
1104
+ border-radius: 12px;
1105
+ max-width: 180px;
1106
+ }
1107
+
1108
+ /* Embedded mode: typing indicator */
1109
+ .aicommerce-embedded .aicommerce-typing {
1110
+ background: var(--aic-bg);
1111
+ border: 1px solid var(--aic-border);
1112
+ border-radius: 12px;
1113
+ padding: 12px 16px;
1114
+ }
1115
+
1116
+ /* Embedded mode responsive - mobile fixes */
1117
+ @media (max-width: 640px) {
1118
+ .aicommerce-embedded .aicommerce-input-container {
1119
+ padding: 6px 10px;
1120
+ gap: 6px;
1121
+ border-radius: 20px;
955
1122
  }
956
1123
 
957
- .aicommerce-embedded .aicommerce-chat {
958
- border-radius: 12px;
1124
+ .aicommerce-embedded .aicommerce-mic,
1125
+ .aicommerce-embedded .aicommerce-send {
1126
+ width: 32px;
1127
+ height: 32px;
1128
+ min-width: 32px;
1129
+ }
1130
+
1131
+ .aicommerce-embedded .aicommerce-mic svg,
1132
+ .aicommerce-embedded .aicommerce-send svg {
1133
+ width: 16px;
1134
+ height: 16px;
1135
+ }
1136
+
1137
+ .aicommerce-embedded .aicommerce-input {
1138
+ font-size: 16px;
1139
+ padding: 6px 4px;
1140
+ }
1141
+
1142
+ .aicommerce-embedded .aicommerce-messages {
1143
+ padding: 12px;
1144
+ }
1145
+
1146
+ .aicommerce-embedded .aicommerce-input-wrapper {
1147
+ padding: 12px;
959
1148
  }
960
1149
  }
1150
+
1151
+ /* ============================================
1152
+ Add to Cart Button Styles
1153
+ ============================================ */
1154
+
1155
+ .aicommerce-add-to-cart {
1156
+ width: 100%;
1157
+ padding: 8px 12px;
1158
+ background: var(--aic-primary);
1159
+ color: white;
1160
+ border: none;
1161
+ border-radius: 6px;
1162
+ font-size: 12px;
1163
+ font-weight: 500;
1164
+ cursor: pointer;
1165
+ margin-top: 8px;
1166
+ transition: all 0.2s;
1167
+ display: flex;
1168
+ align-items: center;
1169
+ justify-content: center;
1170
+ gap: 6px;
1171
+ }
1172
+
1173
+ .aicommerce-add-to-cart:hover {
1174
+ opacity: 0.9;
1175
+ transform: translateY(-1px);
1176
+ }
1177
+
1178
+ .aicommerce-add-to-cart:active {
1179
+ transform: translateY(0);
1180
+ }
1181
+
1182
+ .aicommerce-add-to-cart:disabled {
1183
+ opacity: 0.7;
1184
+ cursor: not-allowed;
1185
+ transform: none;
1186
+ }
1187
+
1188
+ .aicommerce-add-to-cart svg {
1189
+ flex-shrink: 0;
1190
+ }
1191
+
1192
+ /* Spinner animation for loading state */
1193
+ @keyframes aicommerce-spin {
1194
+ to { transform: rotate(360deg); }
1195
+ }
1196
+
1197
+ .aicommerce-spinner {
1198
+ animation: aicommerce-spin 1s linear infinite;
1199
+ }
961
1200
  `;
962
1201
  }
963
1202
  function injectStyles(css) {
@@ -1036,7 +1275,8 @@ function createWidget(config) {
1036
1275
  baseUrl: config.baseUrl || detectBaseUrl(),
1037
1276
  displayMode,
1038
1277
  container: config.container,
1039
- height: config.height || "500px",
1278
+ maxHeight: config.maxHeight || "600px",
1279
+ placeholder: config.placeholder || "Ask me anything about our products...",
1040
1280
  position: config.position || "bottom-right",
1041
1281
  theme: config.theme || "auto",
1042
1282
  primaryColor: config.primaryColor || state.storeConfig?.primaryColor || "#6366f1",
@@ -1045,9 +1285,11 @@ function createWidget(config) {
1045
1285
  zIndex: config.zIndex || 9999,
1046
1286
  buttonText: config.buttonText || "\u{1F4AC}",
1047
1287
  hideLauncher: config.hideLauncher || false,
1288
+ addToCartText: config.addToCartText,
1048
1289
  onOpen: config.onOpen,
1049
1290
  onClose: config.onClose,
1050
1291
  onProductClick: config.onProductClick,
1292
+ onAddToCart: config.onAddToCart,
1051
1293
  onMessage: config.onMessage
1052
1294
  };
1053
1295
  const styles = createWidgetStyles(resolvedConfig);
@@ -1066,7 +1308,7 @@ function createWidget(config) {
1066
1308
  container = document.createElement("div");
1067
1309
  container.id = "aicommerce-widget";
1068
1310
  container.className = `aicommerce-widget aicommerce-embedded aicommerce-theme-${resolvedConfig.theme}`;
1069
- container.style.setProperty("--aic-height", resolvedConfig.height);
1311
+ container.style.setProperty("--aic-max-height", resolvedConfig.maxHeight);
1070
1312
  targetContainer.appendChild(container);
1071
1313
  state.isOpen = true;
1072
1314
  } else {
@@ -1076,16 +1318,78 @@ function createWidget(config) {
1076
1318
  document.body.appendChild(container);
1077
1319
  }
1078
1320
  render();
1079
- state.messages.push({
1080
- role: "assistant",
1081
- content: resolvedConfig.welcomeMessage
1082
- });
1321
+ if (!isEmbedded) {
1322
+ state.messages.push({
1323
+ role: "assistant",
1324
+ content: resolvedConfig.welcomeMessage
1325
+ });
1326
+ }
1083
1327
  state.isLoading = false;
1084
1328
  render();
1085
1329
  }
1330
+ async function addToShopifyCart(variantId, quantity = 1) {
1331
+ const response = await fetch("/cart/add.js", {
1332
+ method: "POST",
1333
+ headers: {
1334
+ "Content-Type": "application/json"
1335
+ },
1336
+ body: JSON.stringify({
1337
+ id: variantId,
1338
+ quantity
1339
+ })
1340
+ });
1341
+ if (!response.ok) {
1342
+ throw new Error("Failed to add to cart");
1343
+ }
1344
+ document.dispatchEvent(new CustomEvent("cart:refresh"));
1345
+ }
1086
1346
  function render() {
1087
1347
  if (!container) return;
1088
1348
  const isEmbedded = resolvedConfig.displayMode === "embedded";
1349
+ const hasUserMessages = state.messages.some((m) => m.role === "user");
1350
+ if (isEmbedded) {
1351
+ container.classList.remove("aicommerce-no-messages", "aicommerce-has-messages");
1352
+ container.classList.add(hasUserMessages ? "aicommerce-has-messages" : "aicommerce-no-messages");
1353
+ }
1354
+ const placeholder = resolvedConfig.placeholder || "Ask me anything about our products...";
1355
+ const inputContainerHtml = `
1356
+ <div class="aicommerce-input-container">
1357
+ ${isEmbedded ? `
1358
+ <textarea
1359
+ class="aicommerce-input"
1360
+ placeholder="${placeholder}"
1361
+ rows="1"
1362
+ ${state.isLoading || state.isRecording ? "disabled" : ""}
1363
+ ></textarea>
1364
+ ` : `
1365
+ <input
1366
+ type="text"
1367
+ class="aicommerce-input"
1368
+ placeholder="${placeholder}"
1369
+ ${state.isLoading || state.isRecording ? "disabled" : ""}
1370
+ />
1371
+ `}
1372
+ <button class="aicommerce-mic ${state.isRecording ? "aicommerce-recording" : ""}" ${state.isLoading ? "disabled" : ""} aria-label="${state.isRecording ? "Stop recording" : "Voice input"}">
1373
+ ${state.isRecording ? `
1374
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
1375
+ <rect x="6" y="6" width="12" height="12" rx="2"/>
1376
+ </svg>
1377
+ ` : `
1378
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1379
+ <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
1380
+ <path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
1381
+ <line x1="12" y1="19" x2="12" y2="23"/>
1382
+ <line x1="8" y1="23" x2="16" y2="23"/>
1383
+ </svg>
1384
+ `}
1385
+ </button>
1386
+ <button class="aicommerce-send" ${state.isLoading || state.isRecording ? "disabled" : ""} aria-label="Send message">
1387
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1388
+ <path d="M22 2L11 13M22 2L15 22L11 13M22 2L2 9L11 13"/>
1389
+ </svg>
1390
+ </button>
1391
+ </div>
1392
+ `;
1089
1393
  const html = `
1090
1394
  ${!isEmbedded && !resolvedConfig.hideLauncher ? `
1091
1395
  <button class="aicommerce-launcher ${state.isOpen ? "aicommerce-hidden" : ""}" aria-label="Open chat">
@@ -1094,6 +1398,7 @@ function createWidget(config) {
1094
1398
  ` : ""}
1095
1399
 
1096
1400
  <div class="aicommerce-chat ${state.isOpen ? "aicommerce-open" : "aicommerce-closed"}">
1401
+ ${!isEmbedded ? `
1097
1402
  <div class="aicommerce-header">
1098
1403
  <div class="aicommerce-header-info">
1099
1404
  <div class="aicommerce-avatar">
@@ -1104,9 +1409,11 @@ function createWidget(config) {
1104
1409
  <span class="aicommerce-status">Online</span>
1105
1410
  </div>
1106
1411
  </div>
1107
- ${!isEmbedded ? `<button class="aicommerce-close" aria-label="Close chat">\u2715</button>` : ""}
1412
+ <button class="aicommerce-close" aria-label="Close chat">\u2715</button>
1108
1413
  </div>
1414
+ ` : ""}
1109
1415
 
1416
+ ${isEmbedded && hasUserMessages ? `
1110
1417
  <div class="aicommerce-messages">
1111
1418
  ${state.messages.map((msg, index) => {
1112
1419
  const isRtl = isArabic(msg.content);
@@ -1145,34 +1452,61 @@ function createWidget(config) {
1145
1452
  </div>
1146
1453
  ` : ""}
1147
1454
  </div>
1455
+ ` : ""}
1148
1456
 
1149
- <div class="aicommerce-input-container">
1150
- <input
1151
- type="text"
1152
- class="aicommerce-input"
1153
- placeholder="Type your message..."
1154
- ${state.isLoading || state.isRecording ? "disabled" : ""}
1155
- />
1156
- <button class="aicommerce-mic ${state.isRecording ? "aicommerce-recording" : ""}" ${state.isLoading ? "disabled" : ""} aria-label="${state.isRecording ? "Stop recording" : "Voice input"}">
1157
- ${state.isRecording ? `
1158
- <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
1159
- <rect x="6" y="6" width="12" height="12" rx="2"/>
1160
- </svg>
1161
- ` : `
1162
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1163
- <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
1164
- <path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
1165
- <line x1="12" y1="19" x2="12" y2="23"/>
1166
- <line x1="8" y1="23" x2="16" y2="23"/>
1167
- </svg>
1168
- `}
1169
- </button>
1170
- <button class="aicommerce-send" ${state.isLoading || state.isRecording ? "disabled" : ""} aria-label="Send message">
1171
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1172
- <path d="M22 2L11 13M22 2L15 22L11 13M22 2L2 9L11 13"/>
1173
- </svg>
1174
- </button>
1457
+ ${!isEmbedded ? `
1458
+ <div class="aicommerce-messages">
1459
+ ${state.messages.map((msg, index) => {
1460
+ const isRtl = isArabic(msg.content);
1461
+ const isUser = msg.role === "user";
1462
+ return `
1463
+ <div class="aicommerce-message aicommerce-${msg.role}">
1464
+ <div class="aicommerce-message-content ${isRtl ? "aicommerce-rtl" : "aicommerce-ltr"}">
1465
+ ${msg.audioUrl ? renderAudioPlayer(msg, index, isUser) : escapeHtml(msg.content)}
1466
+ </div>
1467
+ ${msg.products && msg.products.length > 0 ? `
1468
+ <div class="aicommerce-products">
1469
+ ${msg.products.map((product) => `
1470
+ <div class="aicommerce-product-card" data-product-id="${product.id}" data-variant-id="${product.variantId || ""}">
1471
+ ${product.image || product.imageUrl ? `
1472
+ <img src="${product.image || product.imageUrl}" alt="${escapeHtml(product.name)}" class="aicommerce-product-image" />
1473
+ ` : `
1474
+ <div class="aicommerce-product-placeholder">\u{1F4E6}</div>
1475
+ `}
1476
+ <div class="aicommerce-product-info">
1477
+ <span class="aicommerce-product-name" title="${escapeHtml(product.name)}">${escapeHtml(product.name)}</span>
1478
+ ${product.description ? `<p class="aicommerce-product-desc">${escapeHtml(product.description)}</p>` : ""}
1479
+ <span class="aicommerce-product-price">${formatPrice(product.price, product.currency)}</span>
1480
+ <button class="aicommerce-add-to-cart" data-product-id="${product.id}">
1481
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1482
+ <circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/>
1483
+ <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/>
1484
+ </svg>
1485
+ ${resolvedConfig.addToCartText || "Add to Cart"}
1486
+ </button>
1487
+ </div>
1488
+ </div>
1489
+ `).join("")}
1490
+ </div>
1491
+ ` : ""}
1492
+ </div>
1493
+ `;
1494
+ }).join("")}
1495
+ ${state.isLoading ? `
1496
+ <div class="aicommerce-message aicommerce-assistant">
1497
+ <div class="aicommerce-typing">
1498
+ <span></span><span></span><span></span>
1499
+ </div>
1500
+ </div>
1501
+ ` : ""}
1502
+ </div>
1503
+ ` : ""}
1504
+
1505
+ ${isEmbedded ? `
1506
+ <div class="aicommerce-input-wrapper">
1507
+ ${inputContainerHtml}
1175
1508
  </div>
1509
+ ` : inputContainerHtml}
1176
1510
  </div>
1177
1511
  `;
1178
1512
  container.innerHTML = html;
@@ -1216,18 +1550,40 @@ function createWidget(config) {
1216
1550
  const inputEl = container.querySelector(".aicommerce-input");
1217
1551
  const sendEl = container.querySelector(".aicommerce-send");
1218
1552
  if (inputEl) {
1219
- inputEl.addEventListener("keypress", (e) => {
1220
- if (e.key === "Enter" && inputEl.value.trim()) {
1221
- handleSend(inputEl.value.trim());
1222
- inputEl.value = "";
1553
+ inputEl.addEventListener("keydown", (e) => {
1554
+ const keyEvent = e;
1555
+ if (keyEvent.key === "Enter") {
1556
+ const isTextarea = inputEl instanceof HTMLTextAreaElement;
1557
+ if (isTextarea && keyEvent.shiftKey) {
1558
+ return;
1559
+ }
1560
+ e.preventDefault();
1561
+ const value = inputEl.value.trim();
1562
+ if (value) {
1563
+ handleSend(value);
1564
+ inputEl.value = "";
1565
+ if (isTextarea) {
1566
+ inputEl.style.height = "auto";
1567
+ }
1568
+ }
1223
1569
  }
1224
1570
  });
1571
+ if (inputEl instanceof HTMLTextAreaElement) {
1572
+ inputEl.addEventListener("input", () => {
1573
+ inputEl.style.height = "auto";
1574
+ inputEl.style.height = Math.min(inputEl.scrollHeight, 150) + "px";
1575
+ });
1576
+ }
1225
1577
  }
1226
1578
  if (sendEl && inputEl) {
1227
1579
  sendEl.addEventListener("click", () => {
1228
- if (inputEl.value.trim()) {
1229
- handleSend(inputEl.value.trim());
1580
+ const value = inputEl.value.trim();
1581
+ if (value) {
1582
+ handleSend(value);
1230
1583
  inputEl.value = "";
1584
+ if (inputEl instanceof HTMLTextAreaElement) {
1585
+ inputEl.style.height = "auto";
1586
+ }
1231
1587
  }
1232
1588
  });
1233
1589
  }
@@ -1237,7 +1593,8 @@ function createWidget(config) {
1237
1593
  }
1238
1594
  const productCards = container.querySelectorAll(".aicommerce-product-card");
1239
1595
  productCards.forEach((card) => {
1240
- card.addEventListener("click", () => {
1596
+ card.addEventListener("click", (e) => {
1597
+ if (e.target.closest(".aicommerce-add-to-cart")) return;
1241
1598
  const productId = card.getAttribute("data-product-id");
1242
1599
  const product = state.messages.flatMap((m) => m.products || []).find((p) => p.id === productId);
1243
1600
  if (product) {
@@ -1249,6 +1606,49 @@ function createWidget(config) {
1249
1606
  }
1250
1607
  });
1251
1608
  });
1609
+ const addToCartBtns = container.querySelectorAll(".aicommerce-add-to-cart");
1610
+ addToCartBtns.forEach((btn) => {
1611
+ btn.addEventListener("click", async (e) => {
1612
+ e.stopPropagation();
1613
+ const button = btn;
1614
+ const productCard = button.closest(".aicommerce-product-card");
1615
+ const productId = productCard?.getAttribute("data-product-id");
1616
+ const variantId = productCard?.getAttribute("data-variant-id");
1617
+ const product = state.messages.flatMap((m) => m.products || []).find((p) => p.id === productId);
1618
+ if (!product) return;
1619
+ const originalText = button.innerHTML;
1620
+ button.disabled = true;
1621
+ button.innerHTML = `
1622
+ <svg class="aicommerce-spinner" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1623
+ <circle cx="12" cy="12" r="10" stroke-dasharray="32" stroke-dashoffset="32"/>
1624
+ </svg>
1625
+ Adding...
1626
+ `;
1627
+ try {
1628
+ if (resolvedConfig.onAddToCart) {
1629
+ await resolvedConfig.onAddToCart(product);
1630
+ } else if (variantId && window.Shopify) {
1631
+ await addToShopifyCart(variantId);
1632
+ } else if (product.url) {
1633
+ window.open(product.url, "_blank", "noopener,noreferrer");
1634
+ }
1635
+ button.innerHTML = `
1636
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1637
+ <polyline points="20 6 9 17 4 12"/>
1638
+ </svg>
1639
+ Added!
1640
+ `;
1641
+ setTimeout(() => {
1642
+ button.innerHTML = originalText;
1643
+ button.disabled = false;
1644
+ }, 2e3);
1645
+ } catch (error) {
1646
+ console.error("[AI Commerce] Add to cart failed:", error);
1647
+ button.innerHTML = originalText;
1648
+ button.disabled = false;
1649
+ }
1650
+ });
1651
+ });
1252
1652
  const sliders = container.querySelectorAll(".aicommerce-products");
1253
1653
  sliders.forEach((slider) => {
1254
1654
  let isDown = false;