@yassirbenmoussa/aicommerce-sdk 1.7.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
@@ -902,6 +902,301 @@ function createWidgetStyles(config) {
902
902
  .aicommerce-messages::-webkit-scrollbar-thumb:hover {
903
903
  background: var(--aic-text-secondary);
904
904
  }
905
+
906
+ /* ============================================
907
+ Embedded Mode Styles - ChatGPT Style
908
+ ============================================ */
909
+
910
+ /* Embedded container - fit content with max height */
911
+ #aicommerce-widget.aicommerce-embedded {
912
+ position: relative;
913
+ bottom: auto;
914
+ left: auto;
915
+ right: auto;
916
+ width: 100%;
917
+ height: auto;
918
+ max-height: var(--aic-max-height, 600px);
919
+ display: flex;
920
+ flex-direction: column;
921
+ background: transparent;
922
+ }
923
+
924
+ /* Embedded mode: hide launcher button */
925
+ .aicommerce-embedded .aicommerce-launcher {
926
+ display: none !important;
927
+ }
928
+
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 */
940
+ .aicommerce-embedded .aicommerce-chat {
941
+ position: relative;
942
+ width: 100%;
943
+ height: auto;
944
+ max-width: 100%;
945
+ border-radius: 0;
946
+ background: transparent;
947
+ box-shadow: none;
948
+ transform: none !important;
949
+ opacity: 1 !important;
950
+ pointer-events: auto !important;
951
+ display: flex;
952
+ flex-direction: column;
953
+ }
954
+
955
+ /* Embedded mode: no open/close animations */
956
+ .aicommerce-embedded .aicommerce-chat.aicommerce-closed {
957
+ opacity: 1 !important;
958
+ transform: none !important;
959
+ pointer-events: auto !important;
960
+ }
961
+
962
+ /* Embedded mode: messages area - messages start from bottom, grow upward */
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 {
1040
+ flex: 1;
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;
1054
+ }
1055
+
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;
1122
+ }
1123
+
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;
1148
+ }
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
+ }
905
1200
  `;
906
1201
  }
907
1202
  function injectStyles(css) {
@@ -972,10 +1267,16 @@ function createWidget(config) {
972
1267
  }
973
1268
  async function initialize() {
974
1269
  state.storeConfig = await fetchStoreConfig();
1270
+ const displayMode = config.displayMode || "widget";
1271
+ const isEmbedded = displayMode === "embedded";
975
1272
  resolvedConfig = {
976
1273
  apiKey: config.apiKey,
977
1274
  storeId: config.storeId,
978
1275
  baseUrl: config.baseUrl || detectBaseUrl(),
1276
+ displayMode,
1277
+ container: config.container,
1278
+ maxHeight: config.maxHeight || "600px",
1279
+ placeholder: config.placeholder || "Ask me anything about our products...",
979
1280
  position: config.position || "bottom-right",
980
1281
  theme: config.theme || "auto",
981
1282
  primaryColor: config.primaryColor || state.storeConfig?.primaryColor || "#6366f1",
@@ -984,35 +1285,120 @@ function createWidget(config) {
984
1285
  zIndex: config.zIndex || 9999,
985
1286
  buttonText: config.buttonText || "\u{1F4AC}",
986
1287
  hideLauncher: config.hideLauncher || false,
1288
+ addToCartText: config.addToCartText,
987
1289
  onOpen: config.onOpen,
988
1290
  onClose: config.onClose,
989
1291
  onProductClick: config.onProductClick,
1292
+ onAddToCart: config.onAddToCart,
990
1293
  onMessage: config.onMessage
991
1294
  };
992
1295
  const styles = createWidgetStyles(resolvedConfig);
993
1296
  styleElement = injectStyles(styles);
994
- container = document.createElement("div");
995
- container.id = "aicommerce-widget";
996
- container.className = `aicommerce-widget aicommerce-${resolvedConfig.position} aicommerce-theme-${resolvedConfig.theme}`;
997
- document.body.appendChild(container);
1297
+ if (isEmbedded) {
1298
+ let targetContainer = null;
1299
+ if (typeof config.container === "string") {
1300
+ targetContainer = document.querySelector(config.container);
1301
+ } else if (config.container instanceof HTMLElement) {
1302
+ targetContainer = config.container;
1303
+ }
1304
+ if (!targetContainer) {
1305
+ console.error("[AI Commerce] Embedded mode requires a valid container element or selector");
1306
+ return;
1307
+ }
1308
+ container = document.createElement("div");
1309
+ container.id = "aicommerce-widget";
1310
+ container.className = `aicommerce-widget aicommerce-embedded aicommerce-theme-${resolvedConfig.theme}`;
1311
+ container.style.setProperty("--aic-max-height", resolvedConfig.maxHeight);
1312
+ targetContainer.appendChild(container);
1313
+ state.isOpen = true;
1314
+ } else {
1315
+ container = document.createElement("div");
1316
+ container.id = "aicommerce-widget";
1317
+ container.className = `aicommerce-widget aicommerce-${resolvedConfig.position} aicommerce-theme-${resolvedConfig.theme}`;
1318
+ document.body.appendChild(container);
1319
+ }
998
1320
  render();
999
- state.messages.push({
1000
- role: "assistant",
1001
- content: resolvedConfig.welcomeMessage
1002
- });
1321
+ if (!isEmbedded) {
1322
+ state.messages.push({
1323
+ role: "assistant",
1324
+ content: resolvedConfig.welcomeMessage
1325
+ });
1326
+ }
1003
1327
  state.isLoading = false;
1004
1328
  render();
1005
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
+ }
1006
1346
  function render() {
1007
1347
  if (!container) return;
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
+ `;
1008
1393
  const html = `
1009
- ${!resolvedConfig.hideLauncher ? `
1394
+ ${!isEmbedded && !resolvedConfig.hideLauncher ? `
1010
1395
  <button class="aicommerce-launcher ${state.isOpen ? "aicommerce-hidden" : ""}" aria-label="Open chat">
1011
1396
  <span class="aicommerce-launcher-icon">${resolvedConfig.buttonText}</span>
1012
1397
  </button>
1013
1398
  ` : ""}
1014
1399
 
1015
1400
  <div class="aicommerce-chat ${state.isOpen ? "aicommerce-open" : "aicommerce-closed"}">
1401
+ ${!isEmbedded ? `
1016
1402
  <div class="aicommerce-header">
1017
1403
  <div class="aicommerce-header-info">
1018
1404
  <div class="aicommerce-avatar">
@@ -1025,7 +1411,9 @@ function createWidget(config) {
1025
1411
  </div>
1026
1412
  <button class="aicommerce-close" aria-label="Close chat">\u2715</button>
1027
1413
  </div>
1414
+ ` : ""}
1028
1415
 
1416
+ ${isEmbedded && hasUserMessages ? `
1029
1417
  <div class="aicommerce-messages">
1030
1418
  ${state.messages.map((msg, index) => {
1031
1419
  const isRtl = isArabic(msg.content);
@@ -1064,34 +1452,61 @@ function createWidget(config) {
1064
1452
  </div>
1065
1453
  ` : ""}
1066
1454
  </div>
1455
+ ` : ""}
1067
1456
 
1068
- <div class="aicommerce-input-container">
1069
- <input
1070
- type="text"
1071
- class="aicommerce-input"
1072
- placeholder="Type your message..."
1073
- ${state.isLoading || state.isRecording ? "disabled" : ""}
1074
- />
1075
- <button class="aicommerce-mic ${state.isRecording ? "aicommerce-recording" : ""}" ${state.isLoading ? "disabled" : ""} aria-label="${state.isRecording ? "Stop recording" : "Voice input"}">
1076
- ${state.isRecording ? `
1077
- <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
1078
- <rect x="6" y="6" width="12" height="12" rx="2"/>
1079
- </svg>
1080
- ` : `
1081
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1082
- <path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/>
1083
- <path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
1084
- <line x1="12" y1="19" x2="12" y2="23"/>
1085
- <line x1="8" y1="23" x2="16" y2="23"/>
1086
- </svg>
1087
- `}
1088
- </button>
1089
- <button class="aicommerce-send" ${state.isLoading || state.isRecording ? "disabled" : ""} aria-label="Send message">
1090
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1091
- <path d="M22 2L11 13M22 2L15 22L11 13M22 2L2 9L11 13"/>
1092
- </svg>
1093
- </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}
1094
1508
  </div>
1509
+ ` : inputContainerHtml}
1095
1510
  </div>
1096
1511
  `;
1097
1512
  container.innerHTML = html;
@@ -1135,18 +1550,40 @@ function createWidget(config) {
1135
1550
  const inputEl = container.querySelector(".aicommerce-input");
1136
1551
  const sendEl = container.querySelector(".aicommerce-send");
1137
1552
  if (inputEl) {
1138
- inputEl.addEventListener("keypress", (e) => {
1139
- if (e.key === "Enter" && inputEl.value.trim()) {
1140
- handleSend(inputEl.value.trim());
1141
- 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
+ }
1142
1569
  }
1143
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
+ }
1144
1577
  }
1145
1578
  if (sendEl && inputEl) {
1146
1579
  sendEl.addEventListener("click", () => {
1147
- if (inputEl.value.trim()) {
1148
- handleSend(inputEl.value.trim());
1580
+ const value = inputEl.value.trim();
1581
+ if (value) {
1582
+ handleSend(value);
1149
1583
  inputEl.value = "";
1584
+ if (inputEl instanceof HTMLTextAreaElement) {
1585
+ inputEl.style.height = "auto";
1586
+ }
1150
1587
  }
1151
1588
  });
1152
1589
  }
@@ -1156,7 +1593,8 @@ function createWidget(config) {
1156
1593
  }
1157
1594
  const productCards = container.querySelectorAll(".aicommerce-product-card");
1158
1595
  productCards.forEach((card) => {
1159
- card.addEventListener("click", () => {
1596
+ card.addEventListener("click", (e) => {
1597
+ if (e.target.closest(".aicommerce-add-to-cart")) return;
1160
1598
  const productId = card.getAttribute("data-product-id");
1161
1599
  const product = state.messages.flatMap((m) => m.products || []).find((p) => p.id === productId);
1162
1600
  if (product) {
@@ -1168,6 +1606,49 @@ function createWidget(config) {
1168
1606
  }
1169
1607
  });
1170
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
+ });
1171
1652
  const sliders = container.querySelectorAll(".aicommerce-products");
1172
1653
  sliders.forEach((slider) => {
1173
1654
  let isDown = false;