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