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