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