@yassirbenmoussa/aicommerce-sdk 1.8.0 → 1.9.1

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