@shop-prompter/react 0.1.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,19 @@
1
1
  "use client";
2
2
  import * as React from "react";
3
3
  import { useState, useEffect } from "react";
4
+ import { marked } from "marked";
4
5
  import { ShopPrompterService } from "./shop-prompter.service";
6
+ import ThumbsUpIcon from "./thumbs-up-icon.component";
7
+ import ThumbsDownIcon from "./thumbs-down-icon.component";
8
+ import ProductCardList from "./product-card-list.component";
9
+ const THINKING_SPINNER = "M21 12a9 9 0 1 1-6.219-8.56";
5
10
  const styles = {
6
11
  header: {
7
12
  display: "flex",
8
13
  alignItems: "center",
9
14
  justifyContent: "space-between",
10
15
  padding: "16px",
16
+ borderBottom: "1px solid #e5e7eb",
11
17
  },
12
18
  subHeaderContainer: {
13
19
  display: "flex",
@@ -23,6 +29,7 @@ const styles = {
23
29
  border: "none",
24
30
  cursor: "pointer",
25
31
  transition: "background-color 0.2s, color 0.2s",
32
+ fontFamily: "inherit",
26
33
  onHover: {
27
34
  backgroundColor: "hsl(220 14% 96%)",
28
35
  color: "hsl(220 13% 13%)",
@@ -44,6 +51,7 @@ const styles = {
44
51
  cursor: "pointer",
45
52
  fontSize: "0.875rem",
46
53
  color: "#09090b",
54
+ fontFamily: "inherit",
47
55
  },
48
56
  ButtonSheetButton: {
49
57
  color: "#6b7280",
@@ -53,10 +61,12 @@ const styles = {
53
61
  padding: "0.25rem",
54
62
  fontSize: "1.25rem",
55
63
  transition: "color 0.2s",
64
+ fontFamily: "inherit",
56
65
  },
57
66
  chatContainer: {
58
67
  flex: 1,
59
68
  overflowY: "auto",
69
+ overflowX: "hidden",
60
70
  padding: "1rem",
61
71
  backgroundColor: "white",
62
72
  },
@@ -103,11 +113,16 @@ const styles = {
103
113
  display: "flex",
104
114
  flexDirection: "column",
105
115
  alignItems: "flex-end",
116
+ marginBottom: "8px",
106
117
  },
107
118
  messageContainerAssistant: {
108
119
  display: "flex",
109
120
  flexDirection: "column",
110
121
  alignItems: "flex-start",
122
+ marginBottom: "8px",
123
+ width: "100%",
124
+ maxWidth: "100%",
125
+ boxSizing: "border-box",
111
126
  },
112
127
  messageBubbleUser: {
113
128
  backgroundColor: "#111827",
@@ -118,6 +133,7 @@ const styles = {
118
133
  fontSize: "0.875rem",
119
134
  boxShadow: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
120
135
  maxWidth: "85%",
136
+ textAlign: "left",
121
137
  },
122
138
  messageBubbleAssistant: {
123
139
  backgroundColor: "#f3f4f6",
@@ -128,6 +144,7 @@ const styles = {
128
144
  fontSize: "0.875rem",
129
145
  boxShadow: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
130
146
  maxWidth: "85%",
147
+ textAlign: "left",
131
148
  },
132
149
  thinkingIndicator: {
133
150
  display: "flex",
@@ -210,6 +227,7 @@ const styles = {
210
227
  fontSize: "0.875rem",
211
228
  color: "#111827",
212
229
  padding: "0.25rem 0",
230
+ fontFamily: "inherit",
213
231
  },
214
232
  formButton: {
215
233
  backgroundColor: "transparent",
@@ -223,6 +241,7 @@ const styles = {
223
241
  border: "none",
224
242
  cursor: "pointer",
225
243
  transition: "background-color 0.2s",
244
+ fontFamily: "inherit",
226
245
  },
227
246
  sendButton: {
228
247
  backgroundColor: "#AD59FF1A",
@@ -233,7 +252,161 @@ const styles = {
233
252
  textAlign: "center",
234
253
  marginTop: "0.75rem",
235
254
  },
255
+ backdrop: {
256
+ position: "fixed",
257
+ top: 0,
258
+ left: 0,
259
+ right: 0,
260
+ bottom: 0,
261
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
262
+ backdropFilter: "blur(4px)",
263
+ zIndex: 1000,
264
+ },
265
+ feedbackRow: {
266
+ display: "flex",
267
+ alignItems: "center",
268
+ gap: "0.5rem",
269
+ marginTop: "0.5rem",
270
+ marginLeft: "0.75rem",
271
+ },
272
+ feedbackButton: {
273
+ backgroundColor: "transparent",
274
+ border: "none",
275
+ cursor: "pointer",
276
+ padding: "6px",
277
+ borderRadius: "6px",
278
+ display: "flex",
279
+ alignItems: "center",
280
+ justifyContent: "center",
281
+ transition: "background-color 0.2s",
282
+ },
283
+ warningBanner: {
284
+ backgroundColor: "#fffbeb",
285
+ color: "#b45309",
286
+ border: "1px solid #fde68a",
287
+ borderRadius: "0.375rem",
288
+ padding: "0.25rem 0.5rem",
289
+ fontSize: "0.6875rem",
290
+ fontWeight: 500,
291
+ textAlign: "center",
292
+ margin: "0 0.5rem",
293
+ flex: 1,
294
+ display: "flex",
295
+ alignItems: "center",
296
+ justifyContent: "center",
297
+ },
298
+ insufficientConfigContainer: {
299
+ flex: 1,
300
+ display: "flex",
301
+ flexDirection: "column",
302
+ alignItems: "center",
303
+ justifyContent: "center",
304
+ padding: "2rem",
305
+ textAlign: "center",
306
+ backgroundColor: "#fff5f5",
307
+ color: "#2d3748",
308
+ },
309
+ insufficientConfigIconContainer: {
310
+ backgroundColor: "#fee2e2",
311
+ padding: "1rem",
312
+ borderRadius: "50%",
313
+ marginBottom: "1rem",
314
+ display: "flex",
315
+ alignItems: "center",
316
+ justifyContent: "center",
317
+ boxShadow:
318
+ "0 4px 6px -1px rgba(239, 68, 68, 0.1), 0 2px 4px -1px rgba(239, 68, 68, 0.06)",
319
+ },
320
+ insufficientConfigTitle: {
321
+ fontSize: "1.25rem",
322
+ fontWeight: 700,
323
+ color: "#991b1b",
324
+ marginBottom: "0.5rem",
325
+ },
326
+ insufficientConfigMessage: {
327
+ fontSize: "0.875rem",
328
+ color: "#7f1d1d",
329
+ marginBottom: "1.5rem",
330
+ lineHeight: "1.5",
331
+ maxWidth: "280px",
332
+ },
333
+ insufficientConfigDetails: {
334
+ backgroundColor: "white",
335
+ border: "1px solid #fee2e2",
336
+ borderRadius: "0.5rem",
337
+ padding: "1rem",
338
+ textAlign: "left",
339
+ width: "100%",
340
+ maxWidth: "300px",
341
+ boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.05)",
342
+ },
236
343
  };
344
+ const ANIMATION_STYLES = `
345
+ @keyframes sh-fade-in {
346
+ from {
347
+ opacity: 0;
348
+ transform: translate(-50%, -48%) scale(0.96);
349
+ }
350
+ to {
351
+ opacity: 1;
352
+ transform: translate(-50%, -50%) scale(1);
353
+ }
354
+ }
355
+ @keyframes sh-fade-out {
356
+ from {
357
+ opacity: 1;
358
+ transform: translate(-50%, -50%) scale(1);
359
+ }
360
+ to {
361
+ opacity: 0;
362
+ transform: translate(-50%, -48%) scale(0.96);
363
+ }
364
+ }
365
+ @keyframes sh-backdrop-fade-in {
366
+ from {
367
+ opacity: 0;
368
+ }
369
+ to {
370
+ opacity: 1;
371
+ }
372
+ }
373
+ @keyframes sh-backdrop-fade-out {
374
+ from {
375
+ opacity: 1;
376
+ }
377
+ to {
378
+ opacity: 0;
379
+ }
380
+ }
381
+ .sh-dialog-animate {
382
+ animation: sh-fade-in 0.22s cubic-bezier(0.16, 1, 0.3, 1) forwards;
383
+ }
384
+ .sh-dialog-animate-out {
385
+ animation: sh-fade-out 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
386
+ }
387
+ .sh-backdrop-animate {
388
+ animation: sh-backdrop-fade-in 0.18s ease-out forwards;
389
+ }
390
+ .sh-backdrop-animate-out {
391
+ animation: sh-backdrop-fade-out 0.18s ease-out forwards;
392
+ }
393
+ .sh-feedback-btn {
394
+ color: #a1a1aa;
395
+ border-radius: 4px;
396
+ transition: color 0.2s, background-color 0.2s;
397
+ }
398
+ .sh-feedback-btn:hover {
399
+ background-color: #f4f4f5 !important;
400
+ color: #3f3f46 !important;
401
+ }
402
+ .sh-header-btn {
403
+ transition: all 0.2s;
404
+ }
405
+ .sh-header-btn:hover {
406
+ background-color: #f9fafb !important;
407
+ border-color: #d1d5db !important;
408
+ }
409
+ `;
237
410
  const TRANSLATIONS = {
238
411
  newChat: "New Chat",
239
412
  welcomeToShopPrompter: "Welcome to ShopPrompter",
@@ -241,9 +414,8 @@ const TRANSLATIONS = {
241
414
  "I am your personal AI shopping assistant. Ask me anything about our products!",
242
415
  askMeAnything: "Ask me anything...",
243
416
  thinking: "Thinking...",
244
- showPopularItems: "Show popular items",
245
- compareProducts: "Compare products",
246
- findByBudget: "Find by budget",
417
+ feedbackError:
418
+ "We are sorry, but we could not process your feedback. Please try again later.",
247
419
  };
248
420
  const ICONS = {
249
421
  chat: "https://cdn.jsdelivr.net/gh/lucide-icons/lucide@latest/icons/message-square.svg",
@@ -253,8 +425,6 @@ const ICONS = {
253
425
  "https://cdn.jsdelivr.net/gh/lucide-icons/lucide@latest/icons/fire.svg",
254
426
  compareproducts:
255
427
  "https://cdn.jsdelivr.net/gh/lucide-icons/lucide@latest/icons/box.svg",
256
- findbybudget:
257
- "https://cdn.jsdelivr.net/gh/lucide-icons/lucide@latest/icons/foto.svg",
258
428
  sendHorizontal:
259
429
  "https://cdn.jsdelivr.net/gh/lucide-icons/lucide@latest/icons/send-horizontal.svg",
260
430
  };
@@ -268,42 +438,116 @@ function ShopPrompter(props) {
268
438
 
269
439
  const [chatMessages, setChatMessages] = useState(() => []);
270
440
 
271
- const [showChat, setShowChat] = useState(
272
- () =>
273
- props.chatPosition === "right-panel" || props.chatPosition === "full-page"
274
- );
441
+ const [showChat, setShowChat] = useState(() => false);
442
+
443
+ const [isClosing, setIsClosing] = useState(() => false);
444
+
445
+ const [closeTimeout, setCloseTimeout] = useState(() => null);
275
446
 
276
447
  const [service, setService] = useState(() => null);
277
448
 
278
- const [isHeaderButtonHovered, setIsHeaderButtonHovered] = useState(
279
- () => false
449
+ const [agentExperience, setAgentExperience] = useState(() => null);
450
+
451
+ const [animationClass, setAnimationClass] = useState(() => "");
452
+
453
+ const [backdropAnimationClass, setBackdropAnimationClass] = useState(
454
+ () => ""
280
455
  );
281
456
 
282
- const [agentExperience, setAgentExperience] = useState(() => null);
457
+ function hasToken() {
458
+ const token =
459
+ props.shopPrompterToken || apiConfigObj?.()?.shopPrompterToken;
460
+ return !!token;
461
+ }
462
+
463
+ function isConfigInsufficient() {
464
+ const token =
465
+ props.shopPrompterToken || apiConfigObj?.()?.shopPrompterToken;
466
+ const apiKey = apiConfigObj?.()?.apiKey;
467
+ return !token && !apiKey;
468
+ }
469
+
470
+ function showPriceIfEmptyVal() {
471
+ const val = props.showPriceIfEmpty;
472
+ if (typeof val === "string") {
473
+ return val === "true";
474
+ }
475
+ return val ?? false;
476
+ }
477
+
478
+ function visibleFieldsList() {
479
+ const val = props.visibleFields;
480
+ if (typeof val === "string") {
481
+ try {
482
+ return JSON.parse(val);
483
+ } catch (e) {
484
+ return val
485
+ .split(",")
486
+ .map((s) => s.trim())
487
+ .filter(Boolean);
488
+ }
489
+ }
490
+ return val || [];
491
+ }
492
+
493
+ function themeObj() {
494
+ if (typeof props.theme === "string") {
495
+ try {
496
+ return JSON.parse(props.theme);
497
+ } catch (e) {
498
+ return {};
499
+ }
500
+ }
501
+ return props.theme || {};
502
+ }
503
+
504
+ function prompts() {
505
+ return agentExperience?.predefinedPrompts || [];
506
+ }
507
+
508
+ function apiConfigObj() {
509
+ if (typeof props.apiConfig === "string") {
510
+ try {
511
+ return JSON.parse(props.apiConfig);
512
+ } catch (e) {
513
+ return null;
514
+ }
515
+ }
516
+ return props.apiConfig || null;
517
+ }
283
518
 
284
519
  function containerStyle() {
285
520
  const pos = props.chatPosition || "classic-chatbot";
286
521
  const isFull = pos === "full-page";
287
- const isBottom = pos === "bottom-sheet";
522
+ const isDialog = pos === "dialog";
288
523
  const isClassic = pos === "classic-chatbot";
289
524
  const isRight = pos === "right-panel";
290
525
  return {
291
526
  position: isFull ? "relative" : "fixed",
292
- bottom: isClassic ? "1rem" : isBottom ? "2rem" : "0",
293
- right: isClassic || isRight ? "1rem" : "0",
294
- width: isFull ? "100%" : isBottom ? "90%" : "24rem",
295
- height: isClassic ? "550px" : isBottom ? "700px" : "100%",
296
- maxWidth: isBottom ? "900px" : "none",
297
- left: isBottom ? "50%" : "auto",
298
- transform: isBottom ? "translateX(-50%)" : "none",
299
- zIndex: 50,
300
- backgroundColor: "white",
527
+ bottom: isClassic ? "1rem" : isDialog ? "auto" : "0",
528
+ top: isDialog ? "50%" : "auto",
529
+ right: isClassic || isRight ? "1rem" : isDialog ? "auto" : "0",
530
+ left: isDialog ? "50%" : isClassic || isRight || isFull ? "auto" : "0",
531
+ transform: isDialog ? "translate(-50%, -50%)" : "none",
532
+ width: isFull ? "100%" : isDialog ? "90%" : "24rem",
533
+ height: isClassic
534
+ ? "550px"
535
+ : isDialog
536
+ ? "700px"
537
+ : isFull
538
+ ? "600px"
539
+ : "100%",
540
+ maxWidth: isDialog ? "900px" : "none",
541
+ maxHeight: isDialog ? "90vh" : "none",
542
+ zIndex: isDialog ? 1001 : isClassic || isRight ? 1001 : 50,
543
+ backgroundColor: themeObj().mainBackgroundColor || "white",
301
544
  display: "flex",
302
545
  flexDirection: "column",
303
546
  overflow: "hidden",
304
547
  borderRadius: isFull ? "0" : "0.5rem",
305
548
  border: isFull ? "none" : "1px solid #e5e7eb",
306
549
  boxShadow: isFull ? "none" : "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
550
+ fontFamily: themeObj().fontFamily || undefined,
307
551
  };
308
552
  }
309
553
 
@@ -311,32 +555,33 @@ function ShopPrompter(props) {
311
555
  const pos = props.chatPosition || "classic-chatbot";
312
556
  if (pos === "classic-chatbot") return "sh-classic";
313
557
  if (pos === "right-panel") return "sh-right";
314
- if (pos === "bottom-sheet") return "sh-bottom";
558
+ if (pos === "dialog") {
559
+ return "sh-dialog " + animationClass;
560
+ }
315
561
  return "sh-full";
316
562
  }
317
563
 
318
564
  function messages() {
319
- return chatMessages.map((msg) => ({
320
- ...msg,
321
- safeProductSkus: msg.productSkus || [],
322
- }));
565
+ return chatMessages;
323
566
  }
324
567
 
325
- function showWelcome() {
326
- return chatMessages.length === 0;
568
+ function parseMarkdown(content) {
569
+ if (!content) return "";
570
+ return String(marked.parse(content));
327
571
  }
328
572
 
329
- function getProductImageUrl(sku) {
330
- return "https://placehold.co/100x100?text=" + sku;
573
+ function showWelcome() {
574
+ return chatMessages.length === 0;
331
575
  }
332
576
 
333
- function getProductLink(sku) {
334
- return "/product/" + sku;
577
+ function getShowProducts(message) {
578
+ return message.productSkus && message.productSkus.length > 0;
335
579
  }
336
580
 
337
- function handleSend() {
338
- if (!input.trim() || isLoading) return;
339
- const userQuery = input;
581
+ function handleSend(overrideQuery) {
582
+ const actualQuery = overrideQuery || input;
583
+ if (!actualQuery.trim() || isLoading) return;
584
+ const userQuery = actualQuery;
340
585
  setInput("");
341
586
  const userMsg = {
342
587
  id: Date.now().toString() + "-user",
@@ -344,30 +589,45 @@ function ShopPrompter(props) {
344
589
  content: userQuery,
345
590
  productSkus: [],
346
591
  };
347
- setChatMessages([...chatMessages, userMsg]);
348
592
  const assistantMsg = {
349
593
  id: Date.now().toString() + "-assistant",
350
594
  role: "assistant",
351
595
  content: "",
352
596
  productSkus: [],
353
597
  };
354
- setChatMessages([...chatMessages, assistantMsg]);
598
+ let currentMessages = [...chatMessages, userMsg, assistantMsg];
599
+ setChatMessages(currentMessages);
600
+ setTimeout(() => {
601
+ const inputEl = document.getElementById("shop-prompter-input");
602
+ if (inputEl) {
603
+ inputEl.value = "";
604
+ inputEl.focus();
605
+ }
606
+ }, 0);
355
607
  service?.sendMessage(
356
608
  userQuery,
357
609
  {
358
610
  onTextUpdate: (text) => {
359
- const updatedMessages = [...chatMessages];
611
+ const updatedMessages = [...currentMessages];
360
612
  const lastMsg = updatedMessages[updatedMessages.length - 1];
361
613
  if (lastMsg) {
362
- lastMsg.content = text;
614
+ updatedMessages[updatedMessages.length - 1] = {
615
+ ...lastMsg,
616
+ content: text,
617
+ };
618
+ currentMessages = updatedMessages;
363
619
  setChatMessages(updatedMessages);
364
620
  }
365
621
  },
366
622
  onProductSkus: (skus) => {
367
- const updatedMessages = [...chatMessages];
623
+ const updatedMessages = [...currentMessages];
368
624
  const lastMsg = updatedMessages[updatedMessages.length - 1];
369
625
  if (lastMsg) {
370
- lastMsg.productSkus = skus;
626
+ updatedMessages[updatedMessages.length - 1] = {
627
+ ...lastMsg,
628
+ productSkus: skus,
629
+ };
630
+ currentMessages = updatedMessages;
371
631
  setChatMessages(updatedMessages);
372
632
  }
373
633
  },
@@ -381,16 +641,65 @@ function ShopPrompter(props) {
381
641
  if (props.onCartOperation) {
382
642
  props.onCartOperation(ops);
383
643
  }
644
+ dispatchCartOperation(ops);
384
645
  },
385
646
  },
386
647
  props.cart || []
387
648
  );
388
649
  }
389
650
 
651
+ function dispatchCartOperation(ops) {
652
+ if (typeof window !== "undefined") {
653
+ const root =
654
+ typeof self !== "undefined" &&
655
+ self &&
656
+ "dispatchEvent" in self &&
657
+ !(self instanceof Window)
658
+ ? self
659
+ : null;
660
+ if (root) {
661
+ root.dispatchEvent(
662
+ new CustomEvent("cartOperation", {
663
+ detail: ops,
664
+ })
665
+ );
666
+ }
667
+ }
668
+ }
669
+
390
670
  function toggleChat() {
391
- setShowChat(!showChat);
392
- if (!showChat && props.onClose) {
393
- props.onClose();
671
+ if (showChat) {
672
+ setIsClosing(true);
673
+ setAnimationClass("sh-dialog-animate-out");
674
+ setBackdropAnimationClass("sh-backdrop-animate-out");
675
+ if (closeTimeout) {
676
+ clearTimeout(closeTimeout);
677
+ }
678
+ setCloseTimeout(
679
+ setTimeout(() => {
680
+ setShowChat(false);
681
+ setIsClosing(false);
682
+ setAnimationClass("");
683
+ setBackdropAnimationClass("");
684
+ setCloseTimeout(null);
685
+ if (props.onClose) {
686
+ props.onClose();
687
+ }
688
+ }, 200)
689
+ );
690
+ } else {
691
+ if (closeTimeout) {
692
+ clearTimeout(closeTimeout);
693
+ setCloseTimeout(null);
694
+ }
695
+ setShowChat(true);
696
+ setIsClosing(false);
697
+ setAnimationClass("sh-dialog-animate");
698
+ setBackdropAnimationClass("sh-backdrop-animate");
699
+ setTimeout(() => {
700
+ setAnimationClass("");
701
+ setBackdropAnimationClass("");
702
+ }, 250);
394
703
  }
395
704
  }
396
705
 
@@ -399,16 +708,6 @@ function ShopPrompter(props) {
399
708
  service?.resetSession();
400
709
  }
401
710
 
402
- function onPopularItemsClick() {
403
- setInput(TRANSLATIONS.showPopularItems);
404
- handleSend();
405
- }
406
-
407
- function onCompareProductsClick() {
408
- setInput(TRANSLATIONS.compareProducts);
409
- handleSend();
410
- }
411
-
412
711
  function onInputChange(event) {
413
712
  setInput(event.target.value);
414
713
  }
@@ -419,53 +718,235 @@ function ShopPrompter(props) {
419
718
  }
420
719
  }
421
720
 
422
- function onHeaderButtonMouseEnter() {
423
- setIsHeaderButtonHovered(true);
721
+ function handleTriggerClick(e) {
722
+ if (props.chatPosition !== "dialog") return;
723
+ if (!props.dialogTrigger) return;
724
+ const isTargetClicked =
725
+ typeof props.dialogTrigger === "string"
726
+ ? e.target.closest(props.dialogTrigger)
727
+ : (() => {
728
+ let target = null;
729
+ if (typeof props.dialogTrigger === "object") {
730
+ if ("current" in props.dialogTrigger) {
731
+ target = props.dialogTrigger.current;
732
+ } else {
733
+ target = props.dialogTrigger;
734
+ }
735
+ }
736
+ return target && target.contains && target.contains(e.target);
737
+ })();
738
+ if (isTargetClicked) {
739
+ if (closeTimeout) {
740
+ clearTimeout(closeTimeout);
741
+ setCloseTimeout(null);
742
+ }
743
+ setShowChat(true);
744
+ setIsClosing(false);
745
+ setAnimationClass("sh-dialog-animate");
746
+ setBackdropAnimationClass("sh-backdrop-animate");
747
+ setTimeout(() => {
748
+ setAnimationClass("");
749
+ setBackdropAnimationClass("");
750
+ }, 250);
751
+ }
424
752
  }
425
753
 
426
- function onHeaderButtonMouseLeave() {
427
- setIsHeaderButtonHovered(false);
754
+ function loadCustomFont(fontFamily) {
755
+ if (!fontFamily || typeof window === "undefined") return;
756
+ const systemFonts = [
757
+ "sans-serif",
758
+ "serif",
759
+ "monospace",
760
+ "cursive",
761
+ "fantasy",
762
+ "system-ui",
763
+ "-apple-system",
764
+ "blinkmacsystemfont",
765
+ "segoe ui",
766
+ "roboto",
767
+ "helvetica",
768
+ "arial",
769
+ "inherit",
770
+ ];
771
+ const cleanFont = fontFamily
772
+ .split(",")[0]
773
+ .trim()
774
+ .replace(new RegExp("['\"]", "g"), "");
775
+ if (!cleanFont || systemFonts.includes(cleanFont.toLowerCase())) return;
776
+ const linkId = `shopprompter-font-${cleanFont
777
+ .toLowerCase()
778
+ .replace(new RegExp("\\s+", "g"), "-")}`;
779
+ if (document.getElementById(linkId)) return;
780
+ const link = document.createElement("link");
781
+ link.id = linkId;
782
+ link.rel = "stylesheet";
783
+ const fontNameUrl = cleanFont.replace(new RegExp("\\s+", "g"), "+");
784
+ link.href = `https://fonts.googleapis.com/css2?family=${fontNameUrl}:wght@400;500;600;700&display=swap`;
785
+ document.head.appendChild(link);
428
786
  }
429
787
 
430
- function doSomethingAsync() {
788
+ function loadAgentExperience() {
431
789
  void (async function () {
432
- console.log("## ~ ShopPrompter ~ state.service:", service);
433
790
  if (!service) {
434
- console.warn("Service not initialized yet");
791
+ console.warn(
792
+ "Service not initialized yet! Cannot load AgentExperience."
793
+ );
435
794
  return;
436
795
  }
437
796
  const response = await service?.getAgentExperience();
438
- console.log("## ~ ShopPrompter ~ response:", response);
439
797
  setAgentExperience(response);
440
798
  })();
441
799
  }
442
800
 
801
+ function handleFeedback(messageId, messageContent, newFeedback) {
802
+ const msg = chatMessages.find((m) => m.id === messageId);
803
+ if (!msg) return;
804
+ const currentFeedback = msg.feedback;
805
+ const targetFeedback =
806
+ currentFeedback === newFeedback ? "clear" : newFeedback;
807
+
808
+ // Optimistically update the UI state
809
+ const oldMessages = [...chatMessages];
810
+ setChatMessages(
811
+ chatMessages.map((m) => {
812
+ if (m.id === messageId) {
813
+ return {
814
+ ...m,
815
+ feedback: targetFeedback === "clear" ? null : targetFeedback,
816
+ };
817
+ }
818
+ return m;
819
+ })
820
+ );
821
+ setError(null);
822
+ service
823
+ ?.sendFeedback(messageId, messageContent, targetFeedback)
824
+ .catch((err) => {
825
+ // Revert optimistic update
826
+ setChatMessages(oldMessages);
827
+ setError(err?.message || TRANSLATIONS.feedbackError);
828
+ });
829
+ }
830
+
831
+ function canShowFeedback(message) {
832
+ if (message.role !== "assistant") return false;
833
+ if (!message.content) return false;
834
+ const lastMsg = chatMessages[chatMessages.length - 1];
835
+ if (lastMsg && lastMsg.id === message.id && isLoading) {
836
+ return false;
837
+ }
838
+ return true;
839
+ }
840
+
443
841
  useEffect(() => {
444
- const shopPrompterService = new ShopPrompterService();
445
- setService(shopPrompterService);
842
+ if (
843
+ typeof window !== "undefined" &&
844
+ !document.getElementById("lottie-player-script")
845
+ ) {
846
+ const script = document.createElement("script");
847
+ script.id = "lottie-player-script";
848
+ script.src =
849
+ "https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player";
850
+ document.head.appendChild(script);
851
+ }
852
+ if (themeObj().fontFamily) {
853
+ loadCustomFont(themeObj().fontFamily);
854
+ }
855
+ if (apiConfigObj()) {
856
+ if (!isConfigInsufficient()) {
857
+ const apiConfig = {
858
+ ...apiConfigObj(),
859
+ shopPrompterToken:
860
+ props.shopPrompterToken || apiConfigObj().shopPrompterToken,
861
+ };
862
+ const service = new ShopPrompterService(apiConfig);
863
+ setService(service);
864
+ // Restore persisted messages for the current session.
865
+ const savedMessages = service.loadMessages();
866
+ if (savedMessages.length > 0) {
867
+ setChatMessages(savedMessages);
868
+ }
869
+ }
870
+ } else {
871
+ console.error("ShopPrompter: apiConfig prop is required!");
872
+ }
873
+ if (
874
+ props?.chatPosition === "right-panel" ||
875
+ props?.chatPosition === "full-page"
876
+ ) {
877
+ setShowChat(true);
878
+ }
879
+ if (typeof window !== "undefined") {
880
+ document.addEventListener("click", handleTriggerClick);
881
+ }
446
882
  }, []);
447
883
  useEffect(() => {
448
884
  if (service && !agentExperience) {
449
- doSomethingAsync();
450
- }
451
- if (agentExperience) {
452
- console.log("## ~ ShopPrompter ~ agentExperience updated:", {
453
- greeting: agentExperience.greeting,
454
- agentLogoUrl: agentExperience.agentLogoUrl,
455
- });
885
+ loadAgentExperience();
456
886
  }
457
887
  }, [service, agentExperience]);
888
+ useEffect(() => {
889
+ if (themeObj().fontFamily) {
890
+ loadCustomFont(themeObj().fontFamily);
891
+ }
892
+ }, [themeObj().fontFamily || ""]);
893
+ useEffect(() => {
894
+ if (
895
+ props?.chatPosition === "right-panel" ||
896
+ props?.chatPosition === "full-page"
897
+ ) {
898
+ setShowChat(true);
899
+ } else if (
900
+ props?.chatPosition === "dialog" ||
901
+ props?.chatPosition === "classic-chatbot"
902
+ ) {
903
+ setShowChat(false);
904
+ setIsClosing(false);
905
+ setAnimationClass("");
906
+ setBackdropAnimationClass("");
907
+ }
908
+ }, [props?.chatPosition || ""]);
458
909
 
459
910
  return (
460
911
  <div className="shopprompter-sdk">
912
+ <style dangerouslySetInnerHTML={{ __html: ANIMATION_STYLES }} />
913
+ <div
914
+ onClick={(e) => toggleChat()}
915
+ style={{
916
+ ...styles.backdrop,
917
+ display:
918
+ props.chatPosition === "dialog" && (showChat || isClosing)
919
+ ? "block"
920
+ : "none",
921
+ }}
922
+ className={backdropAnimationClass}
923
+ />
461
924
  {props.chatPosition === "classic-chatbot" ? (
462
925
  <>
463
926
  {!showChat ? (
464
927
  <button
465
- className="fixed bottom-6 right-6 w-16 h-16 bg-blue-600 hover:bg-blue-700 rounded-full shadow-lg z-50 flex items-center justify-center transition-colors border-none cursor-pointer"
466
928
  onClick={(e) => toggleChat()}
467
929
  style={{
468
- boxShadow: "0 0 0 8px rgba(37, 99, 235, 0.2)",
930
+ position: "fixed",
931
+ bottom: "1.5rem",
932
+ right: "1.5rem",
933
+ width: "4rem",
934
+ height: "4rem",
935
+ backgroundColor: themeObj().actionColor || "#2563eb",
936
+ borderRadius: "9999px",
937
+ zIndex: 50,
938
+ display: "flex",
939
+ alignItems: "center",
940
+ justifyContent: "center",
941
+ transition: "background-color 0.2s",
942
+ border: "none",
943
+ cursor: "pointer",
944
+ boxShadow: themeObj().actionColor
945
+ ? `0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05), 0 0 0 8px ${
946
+ themeObj().actionColor
947
+ }33`
948
+ : "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05), 0 0 0 8px rgba(37, 99, 235, 0.2)",
949
+ fontFamily: themeObj().fontFamily || undefined,
469
950
  }}
470
951
  >
471
952
  <img
@@ -480,107 +961,56 @@ function ShopPrompter(props) {
480
961
  ) : null}
481
962
  </>
482
963
  ) : null}
483
- {showChat ? (
964
+ {showChat || isClosing ? (
484
965
  <div style={containerStyle()} className={containerClass()}>
485
- <div style={styles.header}>
486
- <div style={styles.subHeaderContainer}>
487
- <svg
488
- xmlns="http://www.w3.org/2000/svg"
489
- viewBox="0 0 46 47"
490
- width="24"
491
- height="24"
492
- fill="none"
493
- >
494
- <path
495
- fill="url(#a)"
496
- d="M16.452 32.747c-1.587 0-2.874-1.3-2.874-2.903V14.25h5.259V0H5.747C2.576 0 0 2.6 0 5.806v21.139c0 3.208 2.575 5.805 5.748 5.805h10.704v-.003Z"
497
- />
498
- <path
499
- fill="url(#b)"
500
- d="M19.326 0c-3.173 0-5.748 2.6-5.748 5.806v8.444h15.438c1.586 0 2.874 1.3 2.874 2.902v3.924H46V0H19.326Z"
501
- />
502
- <path
503
- fill="url(#c)"
504
- d="M40.252 14.25H29.016c1.586 0 2.874 1.3 2.874 2.902v15.595h-6.751V47h15.117c3.173 0 5.748-2.6 5.748-5.806V20.055c0-3.205-2.575-5.806-5.748-5.806h-.004Z"
505
- />
506
- <path
507
- fill="url(#d)"
508
- d="M8.669 32.75H5.751c-3.173 0-5.747-2.6-5.747-5.805V47h26.142c3.172 0 5.747-2.6 5.747-5.806V32.75H8.67Z"
509
- />
510
- <defs>
511
- <linearGradient
512
- id="a"
513
- x1="14.3"
514
- x2="3.322"
515
- y1="22.494"
516
- y2="4.382"
517
- gradientUnits="userSpaceOnUse"
518
- >
519
- <stop stopColor="#B0145C" />
520
- <stop offset=".37" stopColor="#AE2E9A" />
521
- <stop offset="1" stopColor="#AD59FF" />
522
- </linearGradient>
523
- <linearGradient
524
- id="b"
525
- x1="21.156"
526
- x2="40.431"
527
- y1="21.216"
528
- y2="1.624"
529
- gradientUnits="userSpaceOnUse"
530
- >
531
- <stop offset=".08" stopColor="#E7A2FF" />
532
- <stop offset=".37" stopColor="#E983CD" />
533
- <stop offset=".99" stopColor="#F03A58" />
534
- </linearGradient>
535
- <linearGradient
536
- id="c"
537
- x1="30.464"
538
- x2="46.567"
539
- y1="27.405"
540
- y2="42.472"
541
- gradientUnits="userSpaceOnUse"
542
- >
543
- <stop stopColor="#B0145C" />
544
- <stop offset=".02" stopColor="#B11861" />
545
- <stop offset=".27" stopColor="#C44999" />
546
- <stop offset=".5" stopColor="#D36FC5" />
547
- <stop offset=".71" stopColor="#DE8BE4" />
548
- <stop offset=".88" stopColor="#E49BF8" />
549
- <stop offset="1" stopColor="#E7A2FF" />
550
- </linearGradient>
551
- <linearGradient
552
- id="d"
553
- x1="23.356"
554
- x2="-.618"
555
- y1="44.082"
556
- y2="30.739"
557
- gradientUnits="userSpaceOnUse"
558
- >
559
- <stop stopColor="#AD59FF" />
560
- <stop offset=".1" stopColor="#B455EB" />
561
- <stop offset=".54" stopColor="#D4469C" />
562
- <stop offset=".85" stopColor="#E83D6B" />
563
- <stop offset="1" stopColor="#F03A58" />
564
- </linearGradient>
565
- </defs>
566
- </svg>
567
- <span style={styles.headerText}>ShopPrompter&reg;</span>
568
- </div>
966
+ <div
967
+ style={{
968
+ ...styles.header,
969
+ backgroundColor:
970
+ themeObj().headerBackgroundColor || "transparent",
971
+ }}
972
+ >
973
+ {themeObj().showAgentName !== false ? (
974
+ <div style={styles.subHeaderContainer}>
975
+ {agentExperience?.agentLogoUrl ? (
976
+ <img
977
+ src={agentExperience.agentLogoUrl}
978
+ alt={agentExperience.agentName}
979
+ style={{
980
+ width: "24px",
981
+ height: "24px",
982
+ borderRadius: "4px",
983
+ objectFit: "cover",
984
+ }}
985
+ />
986
+ ) : null}
987
+ {agentExperience?.agentName ? (
988
+ <span style={styles.headerText}>
989
+ {agentExperience.agentName}
990
+ </span>
991
+ ) : null}
992
+ </div>
993
+ ) : (
994
+ <>
995
+ <div style={styles.subHeaderContainer} />
996
+ </>
997
+ )}
998
+ {!hasToken() && apiConfigObj?.()?.apiKey ? (
999
+ <div style={styles.warningBanner}>
1000
+ API Key is only for development purposes
1001
+ </div>
1002
+ ) : null}
569
1003
  <div style={styles.subHeaderContainer}>
570
1004
  <button
1005
+ className="sh-header-btn"
571
1006
  onClick={(e) => handleNewChat()}
572
- onMouseEnter={(e) => onHeaderButtonMouseEnter()}
573
- onMouseLeave={(e) => onHeaderButtonMouseLeave()}
574
- style={{
575
- ...styles.headerButton,
576
- ...(isHeaderButtonHovered ? styles.headerButton.onHover : {}),
577
- }}
1007
+ style={styles.headerButton}
578
1008
  >
579
1009
  {TRANSLATIONS.newChat}
580
1010
  </button>
581
1011
  {props.chatPosition === "classic-chatbot" ||
582
1012
  props.chatPosition === "right-panel" ||
583
- props.chatPosition === "bottom-sheet" ? (
1013
+ props.chatPosition === "dialog" ? (
584
1014
  <button
585
1015
  onClick={(e) => toggleChat()}
586
1016
  style={styles.ButtonSheetButton}
@@ -590,135 +1020,351 @@ function ShopPrompter(props) {
590
1020
  ) : null}
591
1021
  </div>
592
1022
  </div>
593
- <div style={styles.chatContainer}>
594
- {showWelcome() ? (
595
- <div style={styles.welcomeContainer}>
596
- <img
597
- alt="ShopPrompter"
598
- src={
599
- agentExperience?.agentLogoUrl
600
- ? agentExperience.agentLogoUrl
601
- : ICONS.shopprompter
602
- }
1023
+ {isConfigInsufficient() ? (
1024
+ <div style={styles.insufficientConfigContainer}>
1025
+ <div style={styles.insufficientConfigIconContainer}>
1026
+ <svg
1027
+ width="48"
1028
+ height="48"
1029
+ viewBox="0 0 24 24"
1030
+ fill="none"
1031
+ stroke="currentColor"
1032
+ strokeWidth="2"
1033
+ strokeLinecap="round"
1034
+ strokeLinejoin="round"
603
1035
  style={{
604
- width: "64px",
605
- height: "64px",
606
- marginBottom: "24px",
1036
+ color: "#ef4444",
607
1037
  }}
608
- />
609
- <h3 style={styles.welcomeTitle}>
610
- {agentExperience?.greeting?.title ||
611
- TRANSLATIONS.welcomeToShopPrompter}
612
- </h3>
613
- <p style={styles.welcomeDescription}>
614
- {agentExperience?.greeting?.description ||
615
- TRANSLATIONS.welcomeDescription}
616
- </p>
617
- <div style={styles.buttonContainer}>
618
- <button
619
- style={styles.button}
620
- onClick={(e) => onPopularItemsClick()}
1038
+ >
1039
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
1040
+ <path d="M7 11V7a5 5 0 0 1 9.9-1" />
1041
+ </svg>
1042
+ </div>
1043
+ <h3
1044
+ style={{
1045
+ ...styles.insufficientConfigTitle,
1046
+ fontFamily: themeObj().fontFamily || undefined,
1047
+ }}
1048
+ >
1049
+ Configuration Error
1050
+ </h3>
1051
+ <p
1052
+ style={{
1053
+ ...styles.insufficientConfigMessage,
1054
+ fontFamily: themeObj().fontFamily || undefined,
1055
+ }}
1056
+ >
1057
+ ShopPrompter requires a secure token or API key to initialize.
1058
+ Please check your setup.
1059
+ </p>
1060
+ <div
1061
+ style={{
1062
+ ...styles.insufficientConfigDetails,
1063
+ fontFamily: themeObj().fontFamily || undefined,
1064
+ }}
1065
+ >
1066
+ <div
1067
+ style={{
1068
+ fontWeight: 600,
1069
+ marginBottom: "0.25rem",
1070
+ }}
1071
+ >
1072
+ Missing Parameters:
1073
+ </div>
1074
+ <div
1075
+ style={{
1076
+ display: "flex",
1077
+ flexDirection: "column",
1078
+ gap: "0.25rem",
1079
+ fontFamily: "monospace",
1080
+ fontSize: "0.75rem",
1081
+ }}
1082
+ >
1083
+ <span
1084
+ style={{
1085
+ color: "#ef4444",
1086
+ }}
621
1087
  >
622
- {TRANSLATIONS.showPopularItems}
623
- </button>
624
- <button
625
- style={styles.button}
626
- onClick={(e) => onCompareProductsClick()}
1088
+ ✗ shopPrompterToken (not found)
1089
+ </span>
1090
+ <span
1091
+ style={{
1092
+ color: "#ef4444",
1093
+ }}
627
1094
  >
628
- {TRANSLATIONS.compareProducts}
629
- </button>
1095
+ ✗ apiConfig.apiKey (not found)
1096
+ </span>
630
1097
  </div>
631
1098
  </div>
632
- ) : (
633
- <>
634
- {messages()?.map((message) => (
635
- <div
636
- key={message.id}
637
- style={
638
- message.role === "user"
639
- ? styles.messageContainerUser
640
- : styles.messageContainerAssistant
641
- }
642
- >
643
- <div
644
- style={
645
- message.role === "user"
646
- ? styles.messageBubbleUser
647
- : styles.messageBubbleAssistant
1099
+ </div>
1100
+ ) : null}
1101
+ {!isConfigInsufficient() ? (
1102
+ <div
1103
+ style={{
1104
+ display: "flex",
1105
+ flexDirection: "column",
1106
+ flex: 1,
1107
+ overflow: "hidden",
1108
+ }}
1109
+ >
1110
+ <div
1111
+ id="shop-prompter-chat-container"
1112
+ style={{
1113
+ ...styles.chatContainer,
1114
+ backgroundColor:
1115
+ themeObj().mainBackgroundColor ||
1116
+ styles.chatContainer.backgroundColor,
1117
+ }}
1118
+ >
1119
+ {showWelcome() ? (
1120
+ <div style={styles.welcomeContainer}>
1121
+ <img
1122
+ alt="ShopPrompter"
1123
+ src={
1124
+ agentExperience?.agentLogoUrl
1125
+ ? agentExperience.agentLogoUrl
1126
+ : ICONS.shopprompter
648
1127
  }
1128
+ style={{
1129
+ width: "64px",
1130
+ height: "64px",
1131
+ marginBottom: "24px",
1132
+ borderRadius: "12px",
1133
+ objectFit: "cover",
1134
+ }}
1135
+ />
1136
+ <h3
1137
+ style={{
1138
+ ...styles.welcomeTitle,
1139
+ fontFamily:
1140
+ themeObj().fontFamily ||
1141
+ styles.welcomeTitle.fontFamily,
1142
+ }}
649
1143
  >
650
- {message.content}
651
- {!message.content ? (
652
- <>
653
- {message.role === "assistant" ? (
654
- <>
655
- {isLoading ? (
656
- <div style={styles.thinkingIndicator}>
657
- {TRANSLATIONS.thinking}
658
- </div>
659
- ) : null}
660
- </>
661
- ) : null}
662
- </>
663
- ) : null}
1144
+ {agentExperience?.greeting?.title ||
1145
+ TRANSLATIONS.welcomeToShopPrompter}
1146
+ </h3>
1147
+ <p
1148
+ style={{
1149
+ ...styles.welcomeDescription,
1150
+ fontFamily:
1151
+ themeObj().fontFamily ||
1152
+ styles.welcomeDescription.fontFamily,
1153
+ }}
1154
+ >
1155
+ {agentExperience?.greeting?.description ||
1156
+ TRANSLATIONS.welcomeDescription}
1157
+ </p>
1158
+ <div style={styles.buttonContainer}>
1159
+ {prompts()?.map((item) => (
1160
+ <button
1161
+ key={item.prompt}
1162
+ style={styles.button}
1163
+ onClick={(event) => {
1164
+ setInput(item.prompt);
1165
+ handleSend(item.prompt);
1166
+ }}
1167
+ >
1168
+ {item.prompt}
1169
+ </button>
1170
+ ))}
664
1171
  </div>
665
- {message.role === "assistant" ? (
666
- <div style={styles.productCardsContainer}>
667
- {message.safeProductSkus?.map((sku) => (
668
- <div key={sku} style={styles.productCard}>
669
- <div style={styles.productImageContainer}>
670
- <img
671
- src={getProductImageUrl(sku)}
672
- alt={sku}
673
- style={{
674
- maxHeight: "100%",
675
- maxWidth: "100%",
1172
+ </div>
1173
+ ) : null}
1174
+ {!showWelcome() ? (
1175
+ <div
1176
+ style={{
1177
+ width: "100%",
1178
+ }}
1179
+ >
1180
+ {chatMessages?.map((message) => (
1181
+ <div key={message.id}>
1182
+ {message.role === "user" ? (
1183
+ <div style={styles.messageContainerUser}>
1184
+ <div
1185
+ style={{
1186
+ ...styles.messageBubbleUser,
1187
+ backgroundColor:
1188
+ themeObj().userMessageBackgroundColor ||
1189
+ styles.messageBubbleUser.backgroundColor,
1190
+ }}
1191
+ >
1192
+ {message.content}
1193
+ </div>
1194
+ </div>
1195
+ ) : null}
1196
+ {message.role === "assistant" ? (
1197
+ <div style={styles.messageContainerAssistant}>
1198
+ <div
1199
+ style={{
1200
+ ...styles.messageBubbleAssistant,
1201
+ backgroundColor:
1202
+ themeObj().assistantMessageBackgroundColor ||
1203
+ styles.messageBubbleAssistant.backgroundColor,
1204
+ }}
1205
+ >
1206
+ <div
1207
+ dangerouslySetInnerHTML={{
1208
+ __html: parseMarkdown(message.content),
676
1209
  }}
677
1210
  />
1211
+ {!message.content && isLoading ? (
1212
+ <>
1213
+ {agentExperience?.loadingIndicatorUrl ? (
1214
+ <lottie-player
1215
+ background="transparent"
1216
+ speed="1"
1217
+ src={agentExperience.loadingIndicatorUrl}
1218
+ style={{
1219
+ width: "40px",
1220
+ height: "40px",
1221
+ }}
1222
+ loop
1223
+ autoplay
1224
+ />
1225
+ ) : (
1226
+ <div style={styles.thinkingIndicator}>
1227
+ <svg
1228
+ width="24"
1229
+ height="24"
1230
+ viewBox="0 0 24 24"
1231
+ fill="none"
1232
+ stroke="currentColor"
1233
+ strokeWidth="2"
1234
+ strokeLinecap="round"
1235
+ strokeLinejoin="round"
1236
+ style={{
1237
+ color: "#6b7280",
1238
+ }}
1239
+ >
1240
+ <path d={THINKING_SPINNER} />
1241
+ <animateTransform
1242
+ attributeName="transform"
1243
+ type="rotate"
1244
+ from="0 12 12"
1245
+ to="360 12 12"
1246
+ dur="1s"
1247
+ repeatCount="indefinite"
1248
+ />
1249
+ </svg>
1250
+ </div>
1251
+ )}
1252
+ </>
1253
+ ) : null}
678
1254
  </div>
679
- <div style={styles.productInfo}>
680
- <p style={styles.productSku}>{sku}</p>
681
- <a
682
- href={getProductLink(sku)}
683
- style={styles.productLink}
684
- >
685
- VIEW DETAILS
686
- </a>
687
- </div>
1255
+ {canShowFeedback(message) ? (
1256
+ <div style={styles.feedbackRow}>
1257
+ <button
1258
+ className="sh-feedback-btn"
1259
+ onClick={(event) =>
1260
+ handleFeedback(
1261
+ message.id,
1262
+ message.content,
1263
+ "upvote"
1264
+ )
1265
+ }
1266
+ style={styles.feedbackButton}
1267
+ >
1268
+ <ThumbsUpIcon
1269
+ fill={
1270
+ message.feedback === "upvote"
1271
+ ? themeObj().actionColor || "#2563eb"
1272
+ : "none"
1273
+ }
1274
+ stroke={
1275
+ message.feedback === "upvote"
1276
+ ? themeObj().actionColor || "#2563eb"
1277
+ : "currentColor"
1278
+ }
1279
+ />
1280
+ </button>
1281
+ <button
1282
+ className="sh-feedback-btn"
1283
+ onClick={(event) =>
1284
+ handleFeedback(
1285
+ message.id,
1286
+ message.content,
1287
+ "downvote"
1288
+ )
1289
+ }
1290
+ style={styles.feedbackButton}
1291
+ >
1292
+ <ThumbsDownIcon
1293
+ fill={
1294
+ message.feedback === "downvote"
1295
+ ? themeObj().actionColor || "#2563eb"
1296
+ : "none"
1297
+ }
1298
+ stroke={
1299
+ message.feedback === "downvote"
1300
+ ? themeObj().actionColor || "#2563eb"
1301
+ : "currentColor"
1302
+ }
1303
+ />
1304
+ </button>
1305
+ </div>
1306
+ ) : null}
1307
+ {getShowProducts(message) ? (
1308
+ <ProductCardList
1309
+ skus={message.productSkus}
1310
+ defaultLocale={props.defaultLocale}
1311
+ showPriceIfEmpty={showPriceIfEmptyVal()}
1312
+ visibleFields={visibleFieldsList()}
1313
+ />
1314
+ ) : null}
688
1315
  </div>
689
- ))}
1316
+ ) : null}
690
1317
  </div>
691
- ) : null}
1318
+ ))}
692
1319
  </div>
693
- ))}
694
- </>
695
- )}
696
- {error ? (
697
- <div style={styles.errorContainer}>Error: {error}</div>
698
- ) : null}
699
- </div>
700
- <div style={styles.inputSection}>
701
- <div style={styles.inputWrapper}>
702
- <input
703
- type="text"
704
- style={styles.input}
705
- placeholder={TRANSLATIONS.askMeAnything}
706
- value={input}
707
- onInput={(e) => onInputChange(e)}
708
- onKeyDown={(e) => onInputKeyDown(e)}
709
- />
710
- <button
711
- onClick={(e) => handleSend()}
1320
+ ) : null}
1321
+ {error ? (
1322
+ <div style={styles.errorContainer}>Error: {error}</div>
1323
+ ) : null}
1324
+ </div>
1325
+ <div
712
1326
  style={{
713
- ...styles.formButton,
714
- ...styles.sendButton,
1327
+ ...styles.inputSection,
1328
+ backgroundColor:
1329
+ themeObj().mainBackgroundColor ||
1330
+ styles.inputSection.backgroundColor,
715
1331
  }}
716
1332
  >
717
- <img alt="Send" src={ICONS.sendHorizontal} />
718
- </button>
1333
+ <div style={styles.inputWrapper}>
1334
+ <input
1335
+ id="shop-prompter-input"
1336
+ type="text"
1337
+ style={styles.input}
1338
+ placeholder={
1339
+ agentExperience?.hintText || TRANSLATIONS.askMeAnything
1340
+ }
1341
+ value={input}
1342
+ onInput={(e) => onInputChange(e)}
1343
+ onKeyDown={(e) => onInputKeyDown(e)}
1344
+ />
1345
+ <button
1346
+ onClick={(e) => handleSend()}
1347
+ style={{
1348
+ ...styles.formButton,
1349
+ ...styles.sendButton,
1350
+ backgroundColor:
1351
+ themeObj().actionColor ||
1352
+ styles.sendButton.backgroundColor,
1353
+ }}
1354
+ >
1355
+ <img
1356
+ alt="Send"
1357
+ src={ICONS.sendHorizontal}
1358
+ style={{
1359
+ padding: "4px",
1360
+ }}
1361
+ />
1362
+ </button>
1363
+ </div>
1364
+ <p style={styles.disclaimer}>AI content may be inaccurate.</p>
1365
+ </div>
719
1366
  </div>
720
- <p style={styles.disclaimer}>AI content may be inaccurate.</p>
721
- </div>
1367
+ ) : null}
722
1368
  </div>
723
1369
  ) : null}
724
1370
  </div>