@athoscommerce/snap-preact 1.5.1-beta.112 → 1.5.1-beta.116

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.
Files changed (33) hide show
  1. package/dist/cjs/components/src/components/Atoms/Dropdown/Dropdown.d.ts +3 -0
  2. package/dist/cjs/components/src/components/Atoms/Dropdown/Dropdown.d.ts.map +1 -1
  3. package/dist/cjs/components/src/components/Atoms/Dropdown/Dropdown.js +28 -7
  4. package/dist/cjs/components/src/components/Molecules/ChatAttachmentContext/ChatAttachmentContext.d.ts.map +1 -1
  5. package/dist/cjs/components/src/components/Molecules/ChatAttachmentContext/ChatAttachmentContext.js +3 -2
  6. package/dist/cjs/components/src/components/Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage.d.ts.map +1 -1
  7. package/dist/cjs/components/src/components/Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage.js +22 -2
  8. package/dist/cjs/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.d.ts.map +1 -1
  9. package/dist/cjs/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.js +96 -66
  10. package/dist/cjs/components/src/components/Organisms/Chat/Chat.d.ts.map +1 -1
  11. package/dist/cjs/components/src/components/Organisms/Chat/Chat.js +276 -76
  12. package/dist/cjs/components/src/components/Organisms/Chat/Chat.styles.d.ts.map +1 -1
  13. package/dist/cjs/components/src/components/Organisms/Chat/Chat.styles.js +217 -178
  14. package/dist/cjs/components/src/components/Organisms/Chat/hooks/useChatFileUpload.js +4 -4
  15. package/dist/cjs/src/Templates/SnapTemplates.d.ts.map +1 -1
  16. package/dist/cjs/src/Templates/SnapTemplates.js +1 -1
  17. package/dist/esm/components/src/components/Atoms/Dropdown/Dropdown.d.ts +3 -0
  18. package/dist/esm/components/src/components/Atoms/Dropdown/Dropdown.d.ts.map +1 -1
  19. package/dist/esm/components/src/components/Atoms/Dropdown/Dropdown.js +26 -8
  20. package/dist/esm/components/src/components/Molecules/ChatAttachmentContext/ChatAttachmentContext.d.ts.map +1 -1
  21. package/dist/esm/components/src/components/Molecules/ChatAttachmentContext/ChatAttachmentContext.js +3 -2
  22. package/dist/esm/components/src/components/Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage.d.ts.map +1 -1
  23. package/dist/esm/components/src/components/Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage.js +22 -2
  24. package/dist/esm/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.d.ts.map +1 -1
  25. package/dist/esm/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.js +94 -65
  26. package/dist/esm/components/src/components/Organisms/Chat/Chat.d.ts.map +1 -1
  27. package/dist/esm/components/src/components/Organisms/Chat/Chat.js +240 -45
  28. package/dist/esm/components/src/components/Organisms/Chat/Chat.styles.d.ts.map +1 -1
  29. package/dist/esm/components/src/components/Organisms/Chat/Chat.styles.js +73 -8
  30. package/dist/esm/components/src/components/Organisms/Chat/hooks/useChatFileUpload.js +3 -3
  31. package/dist/esm/src/Templates/SnapTemplates.d.ts.map +1 -1
  32. package/dist/esm/src/Templates/SnapTemplates.js +4 -1
  33. package/package.json +12 -12
@@ -83,7 +83,7 @@ var ChatInspirationResultMessage_1 = require("../../Molecules/ChatInspirationRes
83
83
  var ChatProductComparisonMessage_1 = require("../../Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage");
84
84
  var ChatProductQueryMessage_1 = require("../../Molecules/ChatProductQueryMessage/ChatProductQueryMessage");
85
85
  exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
86
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
86
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
87
87
  var globalTheme = (0, providers_1.useTheme)();
88
88
  var globalTreePath = (0, providers_1.useTreePath)();
89
89
  var isMobile = (0, __1.useMediaQuery)('(max-width: 767px)');
@@ -92,7 +92,7 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
92
92
  logo: 'https://cdn.athoscommerce.net/snap/images/Athos%20Commerce_Icon_white.svg',
93
93
  title: 'Athos Conversational Assistant',
94
94
  subtitle: 'Your Guided Discovery Expert',
95
- multiselectFacets: true,
95
+ multiselectFacets: false,
96
96
  disableBubbleSuggestedQuestions: isMobile ? true : false,
97
97
  primaryColorBg: '#253B80',
98
98
  primaryColorFg: '#fff',
@@ -174,33 +174,37 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
174
174
  var chatRef = (0, hooks_1.useRef)(null);
175
175
  // Ref for the mobile secondary panel — needed for swipe-to-dismiss measurements
176
176
  var secondaryRef = (0, hooks_1.useRef)(null);
177
- var _y = (0, hooks_1.useState)(false), mobileProductInfoOpen = _y[0], setMobileProductInfoOpen = _y[1];
178
- var _z = (0, hooks_1.useState)(0), footerHeight = _z[0], setFooterHeight = _z[1];
177
+ var _0 = (0, hooks_1.useState)(false), mobileProductInfoOpen = _0[0], setMobileProductInfoOpen = _0[1];
178
+ var _1 = (0, hooks_1.useState)(0), footerHeight = _1[0], setFooterHeight = _1[1];
179
179
  var dropEnabled = store.features.imageSearch.enabled && !store.loading && !store.blocked && !((_a = store.currentChat) === null || _a === void 0 ? void 0 : _a.sessionLimitReached);
180
- var _0 = (0, useChatFileUpload_1.useChatFileUpload)({
180
+ var _2 = (0, useChatFileUpload_1.useChatFileUpload)({
181
181
  enabled: !!dropEnabled,
182
182
  onUpload: function (files) { return controller.upload(files); },
183
- }), fileInputRef = _0.fileInputRef, isDraggingFile = _0.isDraggingFile, dragHandlers = _0.dragHandlers;
183
+ }), fileInputRef = _2.fileInputRef, isDraggingFile = _2.isDraggingFile, dragHandlers = _2.dragHandlers;
184
184
  // productQuery messages are filtered out of the main chat render (they only drive the side-chat panel),
185
185
  // so don't auto-scroll when one is added — otherwise clicking a product in the side chat scrolls the main thread.
186
186
  var visibleChat = (((_b = store.currentChat) === null || _b === void 0 ? void 0 : _b.chat) || []).filter(function (m) { return m.messageType !== 'productQuery'; });
187
187
  var visibleChatLength = visibleChat.length;
188
188
  var lastVisibleMessage = visibleChat[visibleChat.length - 1];
189
- var _1 = (0, useChatOverflow_1.useChatOverflow)({
189
+ var _3 = (0, useChatOverflow_1.useChatOverflow)({
190
190
  messagesContainerRef: messagesContainerRef,
191
191
  headerRef: headerRef,
192
192
  open: store.open,
193
193
  loading: store.loading,
194
194
  visibleChatLength: visibleChatLength,
195
195
  lastVisibleMessage: lastVisibleMessage,
196
- }), showNewMessages = _1.showNewMessages, setShowNewMessages = _1.setShowNewMessages, messagesOverflow = _1.messagesOverflow, scrollToBottom = _1.scrollToBottom, scrollToBottomIfNear = _1.scrollToBottomIfNear;
196
+ }), showNewMessages = _3.showNewMessages, setShowNewMessages = _3.setShowNewMessages, messagesOverflow = _3.messagesOverflow, scrollToBottom = _3.scrollToBottom, scrollToBottomIfNear = _3.scrollToBottomIfNear;
197
197
  // Re-focus the chat input on desktop after a search completes.
198
198
  // The input is disabled while loading, which causes the browser to drop focus.
199
199
  // On mobile we intentionally skip this so the virtual keyboard dismisses.
200
+ // Only fire on the loading true → false transition — running on mount steals
201
+ // focus away from the Storybook controls whenever the story re-renders.
202
+ var prevLoadingRef = (0, hooks_1.useRef)(store.loading);
200
203
  (0, hooks_1.useEffect)(function () {
201
- if (!store.loading && !isMobile && store.open) {
204
+ if (prevLoadingRef.current && !store.loading && !isMobile && store.open) {
202
205
  controller.focusInput();
203
206
  }
207
+ prevLoadingRef.current = store.loading;
204
208
  }, [store.loading]);
205
209
  // const subProps: ChatSubProps = {};
206
210
  var activeMessage = (_c = store.currentChat) === null || _c === void 0 ? void 0 : _c.activeMessage;
@@ -221,19 +225,34 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
221
225
  var sideChatTypes = isMobile
222
226
  ? __spreadArray(__spreadArray(['inspirationResult'], (isFirstMobileComparison ? ['productComparison'] : []), true), (mobileProductInfoOpen ? ['productQuery'] : []), true) : ['inspirationResult', 'productComparison', 'productQuery'];
223
227
  var shouldShowSideChat = activeMessage && sideChatTypes.includes(activeMessage === null || activeMessage === void 0 ? void 0 : activeMessage.messageType) && ((_d = store.currentChat) === null || _d === void 0 ? void 0 : _d.dismissedSideChatMessageId) !== activeMessage.id;
224
- var styling = (0, utilities_1.mergeStyles)({
225
- mobile: isMobile,
226
- offset: offset,
227
- hasSideChat: !!shouldShowSideChat,
228
- footerHeight: footerHeight,
229
- primaryColorBg: colorPrimary,
230
- primaryColorFg: colorPrimaryText,
231
- primaryAccentColorBg: colorPrimaryAccent,
232
- primaryAccentColorFg: colorPrimaryAccentText,
233
- secondaryAccentColorBg: colorSecondaryAccent,
234
- secondaryAccentColorFg: colorSecondaryAccentText,
235
- position: chatPosition,
236
- }, Chat_styles_1.chatDefaultStyles);
228
+ var hasSideChat = !!shouldShowSideChat;
229
+ var styling = (0, hooks_1.useMemo)(function () {
230
+ return (0, utilities_1.mergeStyles)({
231
+ mobile: isMobile,
232
+ offset: offset,
233
+ hasSideChat: hasSideChat,
234
+ footerHeight: footerHeight,
235
+ primaryColorBg: colorPrimary,
236
+ primaryColorFg: colorPrimaryText,
237
+ primaryAccentColorBg: colorPrimaryAccent,
238
+ primaryAccentColorFg: colorPrimaryAccentText,
239
+ secondaryAccentColorBg: colorSecondaryAccent,
240
+ secondaryAccentColorFg: colorSecondaryAccentText,
241
+ position: chatPosition,
242
+ }, Chat_styles_1.chatDefaultStyles);
243
+ }, [
244
+ isMobile,
245
+ offset,
246
+ hasSideChat,
247
+ footerHeight,
248
+ colorPrimary,
249
+ colorPrimaryText,
250
+ colorPrimaryAccent,
251
+ colorPrimaryAccentText,
252
+ colorSecondaryAccent,
253
+ colorSecondaryAccentText,
254
+ chatPosition,
255
+ ]);
237
256
  // Lock body scroll while chat is open so touch/wheel scrolls don't leak to the page behind
238
257
  (0, hooks_1.useEffect)(function () {
239
258
  if (!store.open)
@@ -281,6 +300,117 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
281
300
  observer.observe(el);
282
301
  return function () { return observer.disconnect(); };
283
302
  }, [store.open, (_e = store.currentChat) === null || _e === void 0 ? void 0 : _e.isExpired]);
303
+ // Prevent the background page from scrolling while the chat is open on touch
304
+ // devices. iOS Safari (real iPad/iPhone) rubber-band-scrolls the body when a
305
+ // touchmove inside `.ss__chat__messages` reaches the top/bottom — and
306
+ // `overscroll-behavior: contain` is ignored there.
307
+ //
308
+ // We don't touch body styles (locking via `position: fixed` on `<body>` leaves
309
+ // iPad Safari in a stuck viewport state where the page can't scroll after the
310
+ // lock is released). Instead we install a `touchmove` handler that:
311
+ // - allows the touchmove if it's inside a scrollable region of the chat
312
+ // and that region isn't at its scroll edge in the swipe direction;
313
+ // - calls `preventDefault()` otherwise — which is what blocks both
314
+ // background scrolling and iOS rubber-band overscroll.
315
+ // Cleanup just removes the listeners, so nothing can leak into the page.
316
+ (0, hooks_1.useEffect)(function () {
317
+ if (!store.open)
318
+ return;
319
+ if (typeof document === 'undefined')
320
+ return;
321
+ var startX = 0;
322
+ var startY = 0;
323
+ var scrollEl = null;
324
+ // Walk up from the touch target to find the first ancestor inside the chat
325
+ // that is actually scrollable in either axis. Stop at the chat root so we
326
+ // never consider elements outside the chat.
327
+ var findScrollableInChat = function (start) {
328
+ var chatRoot = chatRef.current;
329
+ if (!chatRoot)
330
+ return null;
331
+ var el = start;
332
+ while (el && chatRoot.contains(el)) {
333
+ var style = getComputedStyle(el);
334
+ var scrollableY = (style.overflowY === 'auto' || style.overflowY === 'scroll') && el.scrollHeight > el.clientHeight;
335
+ var scrollableX = (style.overflowX === 'auto' || style.overflowX === 'scroll') && el.scrollWidth > el.clientWidth;
336
+ if (scrollableY || scrollableX) {
337
+ return el;
338
+ }
339
+ if (el === chatRoot)
340
+ break;
341
+ el = el.parentElement;
342
+ }
343
+ return null;
344
+ };
345
+ var onTouchStart = function (e) {
346
+ if (!e.touches[0])
347
+ return;
348
+ startX = e.touches[0].clientX;
349
+ startY = e.touches[0].clientY;
350
+ scrollEl = findScrollableInChat(e.target);
351
+ };
352
+ var onTouchMove = function (e) {
353
+ var _a, _b, _c, _d;
354
+ var target = e.target;
355
+ var chatRoot = chatRef.current;
356
+ var inChat = !!(chatRoot && target && chatRoot.contains(target));
357
+ // Touches outside the chat shouldn't scroll the background.
358
+ if (!inChat) {
359
+ if (e.cancelable)
360
+ e.preventDefault();
361
+ return;
362
+ }
363
+ // Inside the chat but no scrollable container — block to prevent body scroll.
364
+ if (!scrollEl) {
365
+ if (e.cancelable)
366
+ e.preventDefault();
367
+ return;
368
+ }
369
+ // Inside a scrollable container — only block when at the edge in the
370
+ // swipe direction, so internal scrolling continues to work. Detect the
371
+ // dominant axis so horizontally-scrollable rows (e.g. the facets row)
372
+ // also get to handle their own touch gestures.
373
+ var currentX = (_b = (_a = e.touches[0]) === null || _a === void 0 ? void 0 : _a.clientX) !== null && _b !== void 0 ? _b : startX;
374
+ var currentY = (_d = (_c = e.touches[0]) === null || _c === void 0 ? void 0 : _c.clientY) !== null && _d !== void 0 ? _d : startY;
375
+ var deltaX = currentX - startX;
376
+ var deltaY = currentY - startY;
377
+ var horizontal = Math.abs(deltaX) > Math.abs(deltaY);
378
+ if (horizontal) {
379
+ var scrollsX = scrollEl.scrollWidth > scrollEl.clientWidth;
380
+ if (!scrollsX) {
381
+ if (e.cancelable)
382
+ e.preventDefault();
383
+ return;
384
+ }
385
+ var atLeft = scrollEl.scrollLeft <= 0;
386
+ var atRight = scrollEl.scrollLeft + scrollEl.clientWidth >= scrollEl.scrollWidth - 1;
387
+ if ((atLeft && deltaX > 0) || (atRight && deltaX < 0)) {
388
+ if (e.cancelable)
389
+ e.preventDefault();
390
+ }
391
+ }
392
+ else {
393
+ var scrollsY = scrollEl.scrollHeight > scrollEl.clientHeight;
394
+ if (!scrollsY) {
395
+ if (e.cancelable)
396
+ e.preventDefault();
397
+ return;
398
+ }
399
+ var atTop = scrollEl.scrollTop <= 0;
400
+ var atBottom = scrollEl.scrollTop + scrollEl.clientHeight >= scrollEl.scrollHeight - 1;
401
+ if ((atTop && deltaY > 0) || (atBottom && deltaY < 0)) {
402
+ if (e.cancelable)
403
+ e.preventDefault();
404
+ }
405
+ }
406
+ };
407
+ document.addEventListener('touchstart', onTouchStart, { passive: true });
408
+ document.addEventListener('touchmove', onTouchMove, { passive: false });
409
+ return function () {
410
+ document.removeEventListener('touchstart', onTouchStart);
411
+ document.removeEventListener('touchmove', onTouchMove);
412
+ };
413
+ }, [store.open]);
284
414
  var HistoryButton = function (props) { return ((0, jsx_runtime_1.jsx)(Button_1.Button, { className: "ss__chat__header__button--history", disabled: props.disabled, icon: {
285
415
  icon: 'history',
286
416
  title: 'Chat History',
@@ -288,7 +418,10 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
288
418
  var HistoryPopup = function (props) { return ((0, jsx_runtime_1.jsxs)("div", { className: "ss__chat__history", children: [(0, jsx_runtime_1.jsxs)("div", { className: "ss__chat__history__header", children: [(0, jsx_runtime_1.jsx)("h4", { children: "History" }), (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__history__header__buttons", children: (0, jsx_runtime_1.jsx)(Button_1.Button, { content: "clear", onClick: function () {
289
419
  controller.store.clearHistory();
290
420
  props.toggleOpen && props.toggleOpen();
291
- } }) })] }), store.chats.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "ss__chat__history__chats", children: store.chats.map(function (chat) { return ((0, jsx_runtime_1.jsx)("div", { className: "ss__chat__history__chats__chat", children: (0, jsx_runtime_1.jsxs)(Button_1.Button, { className: "ss__chat__history__chat__button", onClick: function () {
421
+ } }) })] }), store.chats.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "ss__chat__history__chats", children: store.chats
422
+ .slice()
423
+ .reverse()
424
+ .map(function (chat) { return ((0, jsx_runtime_1.jsx)("div", { className: "ss__chat__history__chats__chat", children: (0, jsx_runtime_1.jsxs)(Button_1.Button, { className: "ss__chat__history__chat__button", onClick: function () {
292
425
  controller.store.switchChat(chat.id);
293
426
  }, disabled: chat.id === store.currentChatId, children: [(0, jsx_runtime_1.jsx)("div", { className: "ss__chat__history__chat__button__text", children: (function () {
294
427
  var lastUserMessage = __spreadArray([], chat.chat, true).reverse().find(function (message) { return message.messageType === 'user'; });
@@ -357,13 +490,26 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
357
490
  boxShadow: '0px 3px 6px 0px rgba(0, 0, 0, 0.2)',
358
491
  borderRadius: '6px',
359
492
  },
360
- }, children: (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__actions__facet__options", children: (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__actions__facet__slider", style: { padding: '12px 16px' }, children: (0, jsx_runtime_1.jsx)(__1.FacetSlider, { facet: props.facet }) }) }) }) }));
493
+ }, children: (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__actions__facet__options", children: (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__actions__facet__slider", style: { padding: '12px 16px' }, children: (0, jsx_runtime_1.jsx)(__1.FacetSlider, { facet: props.facet, onChange: function () {
494
+ if (!multiselectFacets) {
495
+ // FacetSlider fires onChange before its urlManager update; defer
496
+ // so controller.search() reads the updated filter state. Force
497
+ // productSearch with the current filters so a slider tweak doesn't
498
+ // fall through to `general` when hasPendingFacetChanges happens to
499
+ // be false (e.g. the slider returned to the seeded range).
500
+ queueMicrotask(function () {
501
+ return controller.search({
502
+ data: { requestType: 'productSearch', searchFilters: controller.store.searchFilters },
503
+ });
504
+ });
505
+ }
506
+ } }) }) }) }) }));
361
507
  };
362
508
  // TODO: if starting a new chat and it's expired, this button would then disappear
363
509
  if (!controller.store.chatEnabled) {
364
510
  return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {});
365
511
  }
366
- var _2 = (0, useChatGestures_1.useChatGestures)({
512
+ var _4 = (0, useChatGestures_1.useChatGestures)({
367
513
  panelRef: secondaryRef,
368
514
  shouldShowSideChat: !!shouldShowSideChat,
369
515
  activeMessageId: activeMessage === null || activeMessage === void 0 ? void 0 : activeMessage.id,
@@ -375,7 +521,7 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
375
521
  setMobileProductInfoOpen(false);
376
522
  (_b = store.currentChat) === null || _b === void 0 ? void 0 : _b.dismissSideChat();
377
523
  },
378
- }), swipeOffset = _2.swipeOffset, swipeAnimating = _2.swipeAnimating, swipeHandlers = _2.swipeHandlers;
524
+ }), swipeOffset = _4.swipeOffset, swipeAnimating = _4.swipeAnimating, swipeHandlers = _4.swipeHandlers;
379
525
  var requestType = (_f = store.currentChat) === null || _f === void 0 ? void 0 : _f.requestType;
380
526
  var loadingVerbs = (0, hooks_1.useMemo)(function () {
381
527
  switch (requestType) {
@@ -396,11 +542,13 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
396
542
  return undefined;
397
543
  }
398
544
  }, [requestType]);
545
+ var isWelcomeState = (!((_g = store.currentChat) === null || _g === void 0 ? void 0 : _g.chat) || store.currentChat.chat.length === 0) && !!store.welcomeMessage && !((_h = store.currentChat) === null || _h === void 0 ? void 0 : _h.isExpired);
399
546
  return ((0, jsx_runtime_1.jsx)(providers_1.CacheProvider, { children: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", __assign({ ref: chatRef, className: (0, classnames_1.default)('ss__chat', {
400
547
  'ss__chat--open': store.open,
401
548
  'ss__chat--minimized': !store.open,
402
549
  'ss__chat--mobile': isMobile,
403
- }, className, internalClassName) }, styling, { children: [!disableBubbleSuggestedQuestions && !store.open && !store.currentChat && ((_g = store.suggestedQuestions) === null || _g === void 0 ? void 0 : _g.length) > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "ss__chat__suggested-questions", children: store.suggestedQuestions.map(function (question, index) { return ((0, jsx_runtime_1.jsx)("div", { className: "ss__chat__suggested-questions__item", onClick: function () {
550
+ 'ss__chat--welcome': isWelcomeState,
551
+ }, className, internalClassName) }, styling, { children: [!disableBubbleSuggestedQuestions && !store.open && !store.currentChat && ((_j = store.suggestedQuestions) === null || _j === void 0 ? void 0 : _j.length) > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "ss__chat__suggested-questions", children: store.suggestedQuestions.map(function (question, index) { return ((0, jsx_runtime_1.jsx)("div", { className: "ss__chat__suggested-questions__item", onClick: function () {
404
552
  controller.openChat(question);
405
553
  }, children: question }, index)); }) })), (0, jsx_runtime_1.jsx)("button", { type: "button", className: 'ss__chat__bubble', "aria-label": store.open ? 'Close chat' : 'Open chat', "aria-expanded": store.open, onClick: function () { return controller.handlers.button.click(); }, children: (0, jsx_runtime_1.jsx)(__1.Icon, { icon: "chat", title: "Open Chat" }) }), store.open && shouldShowSideChat && activeMessage ? ((0, jsx_runtime_1.jsxs)("div", { ref: secondaryRef, className: (0, classnames_1.default)('ss__chat__secondary'), role: "region", "aria-label": "Conversation details", style: isMobile
406
554
  ? {
@@ -414,7 +562,7 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
414
562
  productQuery: 'Product Information',
415
563
  }[activeMessage.messageType] || null }), (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__header__title__secondary", children: {
416
564
  inspirationResult: 'Choose a style direction to explore',
417
- productComparison: "Comparing ".concat(((_h = activeMessage === null || activeMessage === void 0 ? void 0 : activeMessage.searchResults) === null || _h === void 0 ? void 0 : _h.length) || '', " products"),
565
+ productComparison: "Comparing ".concat(((_k = activeMessage === null || activeMessage === void 0 ? void 0 : activeMessage.searchResults) === null || _k === void 0 ? void 0 : _k.length) || '', " products"),
418
566
  productQuery: 'Complete product details',
419
567
  }[activeMessage.messageType] || null })] }), (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__header__buttons", children: (0, jsx_runtime_1.jsx)(Button_1.Button, { className: "ss__chat__header__button--close", "aria-label": "Close chat", icon: { icon: 'close2', title: 'Close Chat' }, onClick: function () {
420
568
  var _a, _b;
@@ -432,7 +580,7 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
432
580
  } }) })] }), (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__content", children: (0, jsx_runtime_1.jsx)("div", { className: 'ss__chat__messages', children: {
433
581
  inspirationResult: ((0, jsx_runtime_1.jsx)(ChatInspirationResultMessage_1.ChatInspirationResultMessage, __assign({}, subProps.inspirationResultMessage, { chatItem: activeMessage, controller: controller, onProductQuickView: isMobile ? function () { return setMobileProductInfoOpen(true); } : undefined }))),
434
582
  productComparison: ((0, jsx_runtime_1.jsx)(ChatProductComparisonMessage_1.ChatProductComparisonMessage, __assign({}, subProps.productComparisonMessage, { chatItem: activeMessage, controller: controller }))),
435
- productQuery: ((0, jsx_runtime_1.jsx)(ChatProductQueryMessage_1.ChatProductQueryMessage, __assign({}, subProps.productQueryMessage, { chatItem: activeMessage, controller: controller, displayFields: (_k = (_j = controller.config.settings) === null || _j === void 0 ? void 0 : _j.quickview) === null || _k === void 0 ? void 0 : _k.displayFields, primaryColor: colorPrimary, primaryColorText: colorPrimaryText }))),
583
+ productQuery: ((0, jsx_runtime_1.jsx)(ChatProductQueryMessage_1.ChatProductQueryMessage, __assign({}, subProps.productQueryMessage, { chatItem: activeMessage, controller: controller, displayFields: (_m = (_l = controller.config.settings) === null || _l === void 0 ? void 0 : _l.quickview) === null || _m === void 0 ? void 0 : _m.displayFields, primaryColor: colorPrimary, primaryColorText: colorPrimaryText }))),
436
584
  }[activeMessage.messageType] || null }) })] })) : null, store.open ? ((0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__primary', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__header', children: [(0, jsx_runtime_1.jsxs)("div", { className: "ss__chat__header__title", children: [logo ? (0, jsx_runtime_1.jsx)("img", { className: "ss__chat__header__title__logo", src: logo }) : null, (0, jsx_runtime_1.jsxs)("div", { className: "ss__chat__header__title__text", children: [title ? (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__header__title__text__primary", children: title }) : null, subtitle ? (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__header__title__text__secondary", children: subtitle }) : null] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "ss__chat__header__buttons", children: [(0, jsx_runtime_1.jsx)(Button_1.Button
437
585
  // disabled={store.currentChat?.chat && store.currentChat.chat.length <= 1}
438
586
  , {
@@ -455,7 +603,7 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
455
603
  }, children: (0, jsx_runtime_1.jsx)(__1.Icon, { icon: "close-thin", size: "14px" }) }))] })) : null;
456
604
  })(), (0, jsx_runtime_1.jsxs)("div", { className: "ss__chat__content", children: [(0, jsx_runtime_1.jsx)("div", { ref: headerRef, className: (0, classnames_1.default)('ss__chat__content__header', {
457
605
  'ss__chat__content__header--overlay': messagesOverflow,
458
- }), children: ((_l = store.currentChat) === null || _l === void 0 ? void 0 : _l.comparisons.compared) && store.currentChat.comparisons.compared.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__content__header__comparisons', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__content__header__comparisons__header', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__content__header__comparisons__header__title', children: [(0, jsx_runtime_1.jsx)(__1.Icon, { className: 'ss__chat__content__header__comparisons__header__title__icon', icon: 'clipboard' }), (0, jsx_runtime_1.jsxs)("span", { className: 'ss__chat__content__header__comparisons__header__title__text', children: ["Compare Products (", (_m = store.currentChat) === null || _m === void 0 ? void 0 : _m.comparisons.compared.length, "/", (_o = store.currentChat) === null || _o === void 0 ? void 0 : _o.comparisons.maxItems, ")"] })] }), (0, jsx_runtime_1.jsx)("div", { className: 'ss__chat__content__header__comparisons__header__actions', children: (0, jsx_runtime_1.jsx)(Button_1.Button, { onClick: function () { var _a; return (_a = store.currentChat) === null || _a === void 0 ? void 0 : _a.comparisons.reset(); }, children: "clear" }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__content__header__comparisons__content', children: [(0, jsx_runtime_1.jsx)("div", { className: 'ss__chat__content__header__comparisons__content__items', children: Array.from({ length: (_p = store.currentChat) === null || _p === void 0 ? void 0 : _p.comparisons.maxItems }).map(function (_, index) {
606
+ }), children: ((_o = store.currentChat) === null || _o === void 0 ? void 0 : _o.comparisons.compared) && store.currentChat.comparisons.compared.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__content__header__comparisons', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__content__header__comparisons__header', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__content__header__comparisons__header__title', children: [(0, jsx_runtime_1.jsx)(__1.Icon, { className: 'ss__chat__content__header__comparisons__header__title__icon', icon: 'clipboard' }), (0, jsx_runtime_1.jsxs)("span", { className: 'ss__chat__content__header__comparisons__header__title__text', children: ["Compare Products (", (_p = store.currentChat) === null || _p === void 0 ? void 0 : _p.comparisons.compared.length, "/", (_q = store.currentChat) === null || _q === void 0 ? void 0 : _q.comparisons.maxItems, ")"] })] }), (0, jsx_runtime_1.jsx)("div", { className: 'ss__chat__content__header__comparisons__header__actions', children: (0, jsx_runtime_1.jsx)(Button_1.Button, { onClick: function () { var _a; return (_a = store.currentChat) === null || _a === void 0 ? void 0 : _a.comparisons.reset(); }, children: "clear" }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__content__header__comparisons__content', children: [(0, jsx_runtime_1.jsx)("div", { className: 'ss__chat__content__header__comparisons__content__items', children: Array.from({ length: (_r = store.currentChat) === null || _r === void 0 ? void 0 : _r.comparisons.maxItems }).map(function (_, index) {
459
607
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
460
608
  var comparisonItem = (_a = store.currentChat) === null || _a === void 0 ? void 0 : _a.comparisons.compared[index];
461
609
  return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)('ss__chat__content__header__comparisons__content__comparison', {
@@ -475,8 +623,8 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
475
623
  (_a = store.currentChat) === null || _a === void 0 ? void 0 : _a.comparisons.remove(comparisonItem.result.id);
476
624
  }, children: (0, jsx_runtime_1.jsx)(__1.Icon, { icon: 'close-thin', size: '12px' }) })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(__1.Icon, { icon: 'plus-thin' }), (0, jsx_runtime_1.jsx)("div", { className: 'ss__chat__content__header__comparisons__content__comparison--placeholder__text', children: "Add" })] })) }, index));
477
625
  }) }), (0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)('ss__chat__content__header__comparisons__action', {
478
- 'ss__chat__content__header__comparisons__action--hidden': (((_q = store.currentChat) === null || _q === void 0 ? void 0 : _q.comparisons.compared.length) || 0) < 2,
479
- }), children: (0, jsx_runtime_1.jsx)(Button_1.Button, { onClick: function () { return controller.search(); }, icon: { icon: 'compare', title: 'Compare' }, children: "Compare" }) })] })] })) }), (0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__messages', ref: messagesContainerRef, role: "log", "aria-live": "polite", "aria-relevant": "additions", "aria-label": "Chat messages", style: visibleChatLength ? { scrollbarGutter: 'stable' } : undefined, children: [(!((_r = store.currentChat) === null || _r === void 0 ? void 0 : _r.chat) || store.currentChat.chat.length === 0) && store.welcomeMessage && !((_s = store.currentChat) === null || _s === void 0 ? void 0 : _s.isExpired) && ((0, jsx_runtime_1.jsxs)("div", { className: "ss__chat__welcome", children: [(0, jsx_runtime_1.jsx)("div", { className: "ss__chat__welcome__message", children: store.welcomeMessage }), (0, jsx_runtime_1.jsx)(ChatSuggestedQuestions_1.ChatSuggestedQuestions, __assign({}, subProps.suggestedQuestions, { questions: store.suggestedQuestions, controller: controller }))] })), (_t = store.currentChat) === null || _t === void 0 ? void 0 : _t.chat.filter(function (chatItem) { return chatItem.messageType !== 'productQuery'; }).map(function (chatItem, index) { return ((0, jsx_runtime_1.jsx)("div", { className: "ss__chat__message ss__chat__message--".concat(chatItem.messageType), children: {
626
+ 'ss__chat__content__header__comparisons__action--hidden': (((_s = store.currentChat) === null || _s === void 0 ? void 0 : _s.comparisons.compared.length) || 0) < 2,
627
+ }), children: (0, jsx_runtime_1.jsx)(Button_1.Button, { onClick: function () { return controller.search(); }, icon: { icon: 'compare', title: 'Compare' }, children: "Compare" }) })] })] })) }), (0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__messages', ref: messagesContainerRef, role: "log", "aria-live": "polite", "aria-relevant": "additions", "aria-label": "Chat messages", style: visibleChatLength ? { scrollbarGutter: 'stable' } : undefined, children: [(!((_t = store.currentChat) === null || _t === void 0 ? void 0 : _t.chat) || store.currentChat.chat.length === 0) && store.welcomeMessage && !((_u = store.currentChat) === null || _u === void 0 ? void 0 : _u.isExpired) && ((0, jsx_runtime_1.jsxs)("div", { className: "ss__chat__welcome", children: [(0, jsx_runtime_1.jsx)("div", { className: "ss__chat__welcome__message", children: store.welcomeMessage }), (0, jsx_runtime_1.jsx)(ChatSuggestedQuestions_1.ChatSuggestedQuestions, __assign({}, subProps.suggestedQuestions, { questions: store.suggestedQuestions, controller: controller }))] })), (_v = store.currentChat) === null || _v === void 0 ? void 0 : _v.chat.filter(function (chatItem) { return chatItem.messageType !== 'productQuery'; }).map(function (chatItem, index) { return ((0, jsx_runtime_1.jsx)("div", { className: "ss__chat__message ss__chat__message--".concat(chatItem.messageType), children: {
480
628
  user: ((0, jsx_runtime_1.jsx)(ChatMessageUser_1.ChatMessageUser, __assign({}, subProps.messageUser, { chatItem: chatItem, controller: controller, onProductQuickView: isMobile ? function () { return setMobileProductInfoOpen(true); } : undefined, primaryColor: colorPrimary, primaryColorText: colorPrimaryText }))),
481
629
  text: ((0, jsx_runtime_1.jsx)(ChatMessageText_1.ChatMessageText, __assign({}, subProps.messageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onProductQuickView: isMobile ? function () { return setMobileProductInfoOpen(true); } : undefined, sideChatOpen: !!shouldShowSideChat, primaryColor: colorPrimary, primaryColorText: colorPrimaryText }))),
482
630
  content: ((0, jsx_runtime_1.jsx)(ChatMessageText_1.ChatMessageText, __assign({}, subProps.messageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onProductQuickView: isMobile ? function () { return setMobileProductInfoOpen(true); } : undefined, sideChatOpen: !!shouldShowSideChat, primaryColor: colorPrimary, primaryColorText: colorPrimaryText }))),
@@ -500,11 +648,11 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
500
648
  }[chatItem.messageType] || null }, index)); }), (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__messages__end", ref: messagesEndRef }), showNewMessages && messagesOverflow && ((0, jsx_runtime_1.jsxs)("div", { className: "ss__chat__new-messages", onClick: function () {
501
649
  scrollToBottom();
502
650
  setShowNewMessages(false);
503
- }, children: [(0, jsx_runtime_1.jsx)(__1.Icon, { icon: "angle-down", size: "12px" }), (0, jsx_runtime_1.jsx)("span", { children: "New messages" })] }))] }), (0, jsx_runtime_1.jsx)(ChatLoadingIndicator_1.ChatLoadingIndicator, __assign({}, subProps.loadingIndicator, { loading: store.loading, verbs: loadingVerbs })), !((_u = store.currentChat) === null || _u === void 0 ? void 0 : _u.isExpired) ? ((0, jsx_runtime_1.jsxs)("div", __assign({ className: "ss__chat__content__footer", ref: footerRef }, dragHandlers, { children: [store.error && (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__error", children: store.error.message }), ((_v = store.currentChat) === null || _v === void 0 ? void 0 : _v.sessionLimitReached) && ((0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__topic-drift', children: [(0, jsx_runtime_1.jsx)(__1.Icon, { icon: "info", size: "18px", className: 'ss__chat__topic-drift__icon--info' }), (0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__topic-drift__text', children: [(0, jsx_runtime_1.jsx)("span", { children: "Session limit reached" }), (0, jsx_runtime_1.jsx)("span", { children: "This chat has reached its maximum number of interactions. Start a new session to continue." })] }), (0, jsx_runtime_1.jsx)(Button_1.Button, { className: 'ss__chat__topic-drift__button', onClick: function () {
651
+ }, children: [(0, jsx_runtime_1.jsx)(__1.Icon, { icon: "angle-down", size: "12px" }), (0, jsx_runtime_1.jsx)("span", { children: "New messages" })] }))] }), (0, jsx_runtime_1.jsx)(ChatLoadingIndicator_1.ChatLoadingIndicator, __assign({}, subProps.loadingIndicator, { loading: store.loading, verbs: loadingVerbs })), !((_w = store.currentChat) === null || _w === void 0 ? void 0 : _w.isExpired) ? ((0, jsx_runtime_1.jsxs)("div", __assign({ className: "ss__chat__content__footer", ref: footerRef }, dragHandlers, { children: [store.error && (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__error", children: store.error.message }), ((_x = store.currentChat) === null || _x === void 0 ? void 0 : _x.sessionLimitReached) && ((0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__topic-drift', children: [(0, jsx_runtime_1.jsx)(__1.Icon, { icon: "info", size: "18px", className: 'ss__chat__topic-drift__icon--info' }), (0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__topic-drift__text', children: [(0, jsx_runtime_1.jsx)("span", { children: "Session limit reached" }), (0, jsx_runtime_1.jsx)("span", { children: "This chat has reached its maximum number of interactions. Start a new session to continue." })] }), (0, jsx_runtime_1.jsx)(Button_1.Button, { className: 'ss__chat__topic-drift__button', onClick: function () {
504
652
  controller.store.createChat();
505
653
  }, children: "New Session" })] })), store.facets && store.facets.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: 'ss__chat__actions', children: (0, jsx_runtime_1.jsxs)("div", { className: "ss__chat__actions__wrap", children: [(0, jsx_runtime_1.jsxs)("div", { className: "ss__chat__actions__facets-header", children: [(0, jsx_runtime_1.jsx)("div", { className: "ss__chat__actions--title", children: "Filter by:" }), multiselectFacets && store.hasPendingFacetChanges && ((0, jsx_runtime_1.jsx)("div", { className: 'ss__chat__actions__facets-apply ss__chat__actions__facets-apply--active', children: (0, jsx_runtime_1.jsx)(Button_1.Button, { onClick: function () {
506
654
  controller.search();
507
- }, children: "Apply" }) }))] }), (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__actions--facets", children: store.facets.slice(0, 10).map(function (facet, idx) {
655
+ }, children: store.pendingFacetCount > 0 ? "Apply (".concat(store.pendingFacetCount, ")") : 'Clear facets' }) }))] }), (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__actions--facets", children: store.facets.slice(0, 10).map(function (facet, idx) {
508
656
  var _a;
509
657
  // range/slider facets render via FacetSlider; everything else needs at least one value to be useful
510
658
  if (facet.type !== 'range' && !((_a = facet.values) === null || _a === void 0 ? void 0 : _a.length))
@@ -512,7 +660,7 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
512
660
  if (facet.type === 'range') {
513
661
  return (0, jsx_runtime_1.jsx)(SliderFacetDropdown, { facet: facet, label: getFacetButtonLabel(facet) }, idx);
514
662
  }
515
- return ((0, jsx_runtime_1.jsx)("div", { className: "ss__chat__actions__facet ss__chat__actions__facet--".concat(facet.type), children: (0, jsx_runtime_1.jsx)(__1.Dropdown, { usePortal: true, dropUp: true, button: (0, jsx_runtime_1.jsx)(FacetButton, { label: getFacetButtonLabel(facet) }), style: {
663
+ return ((0, jsx_runtime_1.jsx)("div", { className: "ss__chat__actions__facet ss__chat__actions__facet--".concat(facet.type), children: (0, jsx_runtime_1.jsx)(__1.Dropdown, { usePortal: true, dropUp: true, boundaryRef: chatRef, button: (0, jsx_runtime_1.jsx)(FacetButton, { label: getFacetButtonLabel(facet) }), style: {
516
664
  '.ss__dropdown__content': __assign({ width: '150px', maxHeight: '200px', overflowY: 'auto', background: 'white', boxShadow: '0px 3px 6px 0px rgba(0, 0, 0, 0.2)', borderRadius: '6px' }, (multiselectFacets
517
665
  ? {
518
666
  '.ss__chat__actions__facet__option--checkbox': {
@@ -569,6 +717,9 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
569
717
  '&:hover': {
570
718
  background: new utilities_1.Colour(colorPrimary).lightenHex(0.95),
571
719
  },
720
+ '&.ss__chat__actions__facet__option--selected': {
721
+ fontWeight: 'bold',
722
+ },
572
723
  },
573
724
  })),
574
725
  }, children: (0, jsx_runtime_1.jsx)("div", { className: "ss__chat__actions__facet__options", children: facet.type === 'range-buckets'
@@ -592,12 +743,28 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
592
743
  'ss__chat__actions__facet__option__checkbox--checked': isSelected_1,
593
744
  }), children: isSelected_1 && (0, jsx_runtime_1.jsx)(__1.Icon, { icon: "check-thin", size: "10px" }) }), (0, jsx_runtime_1.jsx)("span", { className: "ss__chat__actions__facet__option__label", children: rangeOption.label })] }, optionValue));
594
745
  }
595
- return ((0, jsx_runtime_1.jsx)(Button_1.Button, { onClick: function () {
596
- controller.store.addFacet({
597
- key: facet.field,
598
- value: optionValue,
746
+ return ((0, jsx_runtime_1.jsx)(Button_1.Button, { className: (0, classnames_1.default)({
747
+ 'ss__chat__actions__facet__option--selected': controller.store.isFacetSelected(facet.field, optionValue),
748
+ }), onClick: function () {
749
+ // Non-multiselect: clicking an already-selected value toggles it off.
750
+ // `addFacet` is idempotent against an existing urlManager value, so without
751
+ // the toggle the state wouldn't change, `hasPendingFacetChanges` would stay
752
+ // false, and the params getter would fall through to `general`.
753
+ if (controller.store.isFacetSelected(facet.field, optionValue)) {
754
+ controller.store.removeFacet(facet.field, optionValue);
755
+ }
756
+ else {
757
+ controller.store.addFacet({
758
+ key: facet.field,
759
+ value: optionValue,
760
+ });
761
+ }
762
+ // Force productSearch with the current filter state rather than relying
763
+ // on the params getter's hasPendingFacetChanges promotion — a facet click
764
+ // in the chat is unambiguously a search intent.
765
+ controller.search({
766
+ data: { requestType: 'productSearch', searchFilters: controller.store.searchFilters },
599
767
  });
600
- controller.search();
601
768
  }, children: rangeOption.label }, optionValue));
602
769
  })
603
770
  : facet.values.map(function (option) {
@@ -619,12 +786,28 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
619
786
  'ss__chat__actions__facet__option__checkbox--checked': isSelected_2,
620
787
  }), children: isSelected_2 && (0, jsx_runtime_1.jsx)(__1.Icon, { icon: "check-thin", size: "10px" }) }), (0, jsx_runtime_1.jsx)("span", { className: "ss__chat__actions__facet__option__label", children: valueOption.label })] }, optionValue));
621
788
  }
622
- return ((0, jsx_runtime_1.jsx)(Button_1.Button, { onClick: function () {
623
- controller.store.addFacet({
624
- key: facet.field,
625
- value: optionValue,
789
+ return ((0, jsx_runtime_1.jsx)(Button_1.Button, { className: (0, classnames_1.default)({
790
+ 'ss__chat__actions__facet__option--selected': controller.store.isFacetSelected(facet.field, optionValue),
791
+ }), onClick: function () {
792
+ // Non-multiselect: clicking an already-selected value toggles it off.
793
+ // `addFacet` is idempotent against an existing urlManager value, so without
794
+ // the toggle the state wouldn't change, `hasPendingFacetChanges` would stay
795
+ // false, and the params getter would fall through to `general`.
796
+ if (controller.store.isFacetSelected(facet.field, optionValue)) {
797
+ controller.store.removeFacet(facet.field, optionValue);
798
+ }
799
+ else {
800
+ controller.store.addFacet({
801
+ key: facet.field,
802
+ value: optionValue,
803
+ });
804
+ }
805
+ // Force productSearch with the current filter state rather than relying
806
+ // on the params getter's hasPendingFacetChanges promotion — a facet click
807
+ // in the chat is unambiguously a search intent.
808
+ controller.search({
809
+ data: { requestType: 'productSearch', searchFilters: controller.store.searchFilters },
626
810
  });
627
- controller.search();
628
811
  }, children: valueOption.label }, optionValue));
629
812
  }) }) }, facet.field) }, idx));
630
813
  }) })] }) })), (function () {
@@ -653,12 +836,11 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
653
836
  id: result === null || result === void 0 ? void 0 : result.id,
654
837
  name: ((_b = (_a = d === null || d === void 0 ? void 0 : d.mappings) === null || _a === void 0 ? void 0 : _a.core) === null || _b === void 0 ? void 0 : _b.name) || '',
655
838
  imageUrl: ((_d = (_c = d === null || d === void 0 ? void 0 : d.mappings) === null || _c === void 0 ? void 0 : _c.core) === null || _d === void 0 ? void 0 : _d.thumbnailImageUrl) || ((_f = (_e = d === null || d === void 0 ? void 0 : d.mappings) === null || _e === void 0 ? void 0 : _e.core) === null || _f === void 0 ? void 0 : _f.imageUrl) || ((_h = (_g = d === null || d === void 0 ? void 0 : d.mappings) === null || _g === void 0 ? void 0 : _g.core) === null || _h === void 0 ? void 0 : _h.parentImageUrl) || '',
656
- onClick: isMobile
657
- ? function () {
658
- controller.productQuickView(result);
839
+ onClick: function () {
840
+ controller.productQuickView(result);
841
+ if (isMobile)
659
842
  setMobileProductInfoOpen(true);
660
- }
661
- : undefined,
843
+ },
662
844
  };
663
845
  })
664
846
  : showCommittedComparisons
@@ -669,34 +851,29 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
669
851
  id: (_b = comparisonItem.result) === null || _b === void 0 ? void 0 : _b.id,
670
852
  name: ((_d = (_c = d === null || d === void 0 ? void 0 : d.mappings) === null || _c === void 0 ? void 0 : _c.core) === null || _d === void 0 ? void 0 : _d.name) || '',
671
853
  imageUrl: ((_f = (_e = d === null || d === void 0 ? void 0 : d.mappings) === null || _e === void 0 ? void 0 : _e.core) === null || _f === void 0 ? void 0 : _f.thumbnailImageUrl) || ((_h = (_g = d === null || d === void 0 ? void 0 : d.mappings) === null || _g === void 0 ? void 0 : _g.core) === null || _h === void 0 ? void 0 : _h.imageUrl) || ((_k = (_j = d === null || d === void 0 ? void 0 : d.mappings) === null || _j === void 0 ? void 0 : _j.core) === null || _k === void 0 ? void 0 : _k.parentImageUrl) || '',
672
- onClick: isMobile
673
- ? function () {
674
- controller.productQuickView(comparisonItem.result);
854
+ onClick: function () {
855
+ controller.productQuickView(comparisonItem.result);
856
+ if (isMobile)
675
857
  setMobileProductInfoOpen(true);
676
- }
677
- : undefined,
858
+ },
678
859
  };
679
860
  })
680
861
  : [];
681
- var productItems = productAttachments.map(function (item) { return ({
682
- id: item.id,
683
- name: item.name || '',
684
- imageUrl: item.thumbnailUrl || '',
685
- onClick: isMobile
686
- ? function () {
687
- setMobileProductInfoOpen(true);
688
- }
689
- : function () {
690
- var _a, _b, _c;
691
- (_a = store.currentChat) === null || _a === void 0 ? void 0 : _a.setActiveMessage(((_c = (_b = store.currentChat) === null || _b === void 0 ? void 0 : _b.activeMessage) === null || _c === void 0 ? void 0 : _c.id) || '');
692
- },
693
- onRemove: function () {
862
+ var productItems = productAttachments.map(function (item) {
863
+ var remove = function () {
694
864
  var _a, _b;
695
865
  (_a = store.currentChat) === null || _a === void 0 ? void 0 : _a.attachments.remove(item.id);
696
866
  setMobileProductInfoOpen(false);
697
867
  (_b = store.currentChat) === null || _b === void 0 ? void 0 : _b.dismissSideChat();
698
- },
699
- }); });
868
+ };
869
+ return {
870
+ id: item.id,
871
+ name: item.name || '',
872
+ imageUrl: item.thumbnailUrl || '',
873
+ onClick: remove,
874
+ onRemove: remove,
875
+ };
876
+ });
700
877
  var imageItems = imageAttachments.map(function (item) {
701
878
  var _a;
702
879
  return ({
@@ -709,7 +886,7 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
709
886
  onRemove: function () { var _a; return (_a = store.currentChat) === null || _a === void 0 ? void 0 : _a.attachments.remove(item.id); },
710
887
  });
711
888
  });
712
- var productTitle = productItems.length > 1 ? 'Compare these products' : 'Ask about this product' + (isMobile ? '' : ' (click for details)');
889
+ var productTitle = productItems.length > 1 ? 'Compare these products' : 'Ask about this product';
713
890
  var hasImageError = imageItems.some(function (item) { return item.hasError; });
714
891
  var imageTitle = imageItems.length > 0 && !hasImageError ? 'Find products similar to this image:' : '';
715
892
  return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [comparisonItems.length > 0 && ((0, jsx_runtime_1.jsx)(ChatAttachmentContext_1.ChatAttachmentContext, __assign({}, subProps.attachmentContext, { title: 'Compare these products', items: comparisonItems, onClose: function () {
@@ -738,21 +915,44 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
738
915
  : 'Start a fresh session for better assistance, or keep going in this one?';
739
916
  var buttonText = isScopeRedirect ? '' : 'New Session';
740
917
  return ((0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__topic-drift', children: [(0, jsx_runtime_1.jsx)(__1.Icon, { icon: "info", size: "18px", className: 'ss__chat__topic-drift__icon--info' }), (0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__topic-drift__text', children: [(0, jsx_runtime_1.jsx)("span", { children: primaryText }), (0, jsx_runtime_1.jsx)("span", { children: secondaryText })] }), buttonText && ((0, jsx_runtime_1.jsx)(Button_1.Button, { className: 'ss__chat__topic-drift__button', onClick: function () {
741
- var _a, _b;
918
+ var _a, _b, _c;
742
919
  if (isScopeRedirect) {
743
920
  (_a = store.currentChat) === null || _a === void 0 ? void 0 : _a.dismissTopicDrift();
744
921
  return;
745
922
  }
746
- var messageText = (_b = store.currentChat) === null || _b === void 0 ? void 0 : _b.handleTopicDrift();
923
+ // Capture image attachments from the current chat before handleTopicDrift /
924
+ // createChat tear down the conversation — an imageSearch that drifted needs
925
+ // the same image attached to the carried-over message in the new session,
926
+ // otherwise params would fall back to a `general` request without the image.
927
+ var previousImageAttachments = (((_b = store.currentChat) === null || _b === void 0 ? void 0 : _b.attachments.attached) || [])
928
+ .filter(function (item) { return item.type === 'image' && item.state !== 'error'; })
929
+ .map(function (item) {
930
+ var img = item;
931
+ return {
932
+ type: 'image',
933
+ base64: img.base64,
934
+ fileName: img.fileName,
935
+ imageId: img.imageId,
936
+ imageUrl: img.imageUrl,
937
+ thumbnailUrl: img.thumbnailUrl,
938
+ // Already uploaded — skip the 'loading' default state.
939
+ state: 'attached',
940
+ };
941
+ });
942
+ var messageText = (_c = store.currentChat) === null || _c === void 0 ? void 0 : _c.handleTopicDrift();
747
943
  if (messageText) {
748
944
  controller.store.createChat();
945
+ previousImageAttachments.forEach(function (cfg) {
946
+ var _a;
947
+ (_a = controller.store.currentChat) === null || _a === void 0 ? void 0 : _a.attachments.add(cfg);
948
+ });
749
949
  controller.search({ data: { message: messageText } });
750
950
  }
751
951
  }, children: buttonText })), (0, jsx_runtime_1.jsx)("span", { onClick: function () {
752
952
  var _a;
753
953
  (_a = store.currentChat) === null || _a === void 0 ? void 0 : _a.dismissTopicDrift();
754
954
  }, children: (0, jsx_runtime_1.jsx)(__1.Icon, { icon: "close-thin", size: "14px", className: 'ss__chat__topic-drift__icon--close' }) })] }));
755
- })(), (0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__input', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__input__input', children: [(0, jsx_runtime_1.jsx)("input", { type: "text", name: "ss-chat-input", disabled: store.loading || store.blocked || ((_w = store.currentChat) === null || _w === void 0 ? void 0 : _w.sessionLimitReached), placeholder: (function () {
955
+ })(), (0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__input', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'ss__chat__input__input', children: [(0, jsx_runtime_1.jsx)("input", { type: "text", name: "ss-chat-input", disabled: store.loading || store.blocked || ((_y = store.currentChat) === null || _y === void 0 ? void 0 : _y.sessionLimitReached), placeholder: (function () {
756
956
  var _a, _b, _c;
757
957
  var comparedCount = ((_a = store.currentChat) === null || _a === void 0 ? void 0 : _a.comparisons.compared.length) || 0;
758
958
  var committedCount = ((_b = store.currentChat) === null || _b === void 0 ? void 0 : _b.comparisons.committed.length) || 0;
@@ -780,7 +980,7 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
780
980
  if (e.keyCode === 13 && isMobile && mobileProductInfoOpen) {
781
981
  setMobileProductInfoOpen(false);
782
982
  }
783
- }, value: controller.store.inputValue }), store.features.imageSearch.enabled && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Button_1.Button, { className: 'ss__chat__upload-button', disabled: store.loading || store.blocked || ((_x = store.currentChat) === null || _x === void 0 ? void 0 : _x.sessionLimitReached), onClick: function () { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, icon: { icon: 'image', title: 'Upload Image' } }), (0, jsx_runtime_1.jsx)("input", { ref: fileInputRef, onChange: function (e) { return __awaiter(void 0, void 0, void 0, function () {
983
+ }, value: controller.store.inputValue }), store.features.imageSearch.enabled && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(Button_1.Button, { className: 'ss__chat__upload-button', disabled: store.loading || store.blocked || ((_z = store.currentChat) === null || _z === void 0 ? void 0 : _z.sessionLimitReached), onClick: function () { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, icon: { icon: 'image', title: 'Upload Image' } }), (0, jsx_runtime_1.jsx)("input", { ref: fileInputRef, onChange: function (e) { return __awaiter(void 0, void 0, void 0, function () {
784
984
  return __generator(this, function (_a) {
785
985
  switch (_a.label) {
786
986
  case 0: return [4 /*yield*/, controller.upload(e.target.files)];
@@ -791,7 +991,7 @@ exports.ChatOrganism = (0, mobx_react_lite_1.observer)(function (properties) {
791
991
  return [2 /*return*/];
792
992
  }
793
993
  });
794
- }); }, multiple: true, type: "file", accept: "image/*", className: "ss__chat__input__input__file" })] }))] }), (0, jsx_runtime_1.jsx)("div", { className: 'ss__chat__input__actions', children: (function () {
994
+ }); }, type: "file", accept: "image/*", className: "ss__chat__input__input__file" })] }))] }), (0, jsx_runtime_1.jsx)("div", { className: 'ss__chat__input__actions', children: (function () {
795
995
  var _a, _b;
796
996
  var hasImageAttachment = (((_a = store.currentChat) === null || _a === void 0 ? void 0 : _a.attachments.attached) || []).some(function (item) { return item.type === 'image' && !item.error; });
797
997
  var messageEmpty = !controller.store.inputValue.trim();