@shop-prompter/react 0.1.1 → 1.0.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,9 @@ 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
417
  findByBudget: "Find by budget",
418
+ feedbackError:
419
+ "We are sorry, but we could not process your feedback. Please try again later.",
247
420
  };
248
421
  const ICONS = {
249
422
  chat: "https://cdn.jsdelivr.net/gh/lucide-icons/lucide@latest/icons/message-square.svg",
@@ -268,42 +441,93 @@ function ShopPrompter(props) {
268
441
 
269
442
  const [chatMessages, setChatMessages] = useState(() => []);
270
443
 
271
- const [showChat, setShowChat] = useState(
272
- () =>
273
- props.chatPosition === "right-panel" || props.chatPosition === "full-page"
274
- );
444
+ const [showChat, setShowChat] = useState(() => false);
445
+
446
+ const [isClosing, setIsClosing] = useState(() => false);
447
+
448
+ const [closeTimeout, setCloseTimeout] = useState(() => null);
275
449
 
276
450
  const [service, setService] = useState(() => null);
277
451
 
278
- const [isHeaderButtonHovered, setIsHeaderButtonHovered] = useState(
279
- () => false
452
+ const [agentExperience, setAgentExperience] = useState(() => null);
453
+
454
+ const [animationClass, setAnimationClass] = useState(() => "");
455
+
456
+ const [backdropAnimationClass, setBackdropAnimationClass] = useState(
457
+ () => ""
280
458
  );
281
459
 
282
- const [agentExperience, setAgentExperience] = useState(() => null);
460
+ function hasToken() {
461
+ const token =
462
+ props.shopPrompterToken || apiConfigObj?.()?.shopPrompterToken;
463
+ return !!token;
464
+ }
465
+
466
+ function isConfigInsufficient() {
467
+ const token =
468
+ props.shopPrompterToken || apiConfigObj?.()?.shopPrompterToken;
469
+ const apiKey = apiConfigObj?.()?.apiKey;
470
+ return !token && !apiKey;
471
+ }
472
+
473
+ function themeObj() {
474
+ if (typeof props.theme === "string") {
475
+ try {
476
+ return JSON.parse(props.theme);
477
+ } catch (e) {
478
+ return {};
479
+ }
480
+ }
481
+ return props.theme || {};
482
+ }
483
+
484
+ function prompts() {
485
+ return agentExperience?.predefinedPrompts || [];
486
+ }
487
+
488
+ function apiConfigObj() {
489
+ if (typeof props.apiConfig === "string") {
490
+ try {
491
+ return JSON.parse(props.apiConfig);
492
+ } catch (e) {
493
+ return null;
494
+ }
495
+ }
496
+ return props.apiConfig || null;
497
+ }
283
498
 
284
499
  function containerStyle() {
285
500
  const pos = props.chatPosition || "classic-chatbot";
286
501
  const isFull = pos === "full-page";
287
- const isBottom = pos === "bottom-sheet";
502
+ const isDialog = pos === "dialog";
288
503
  const isClassic = pos === "classic-chatbot";
289
504
  const isRight = pos === "right-panel";
290
505
  return {
291
506
  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",
507
+ bottom: isClassic ? "1rem" : isDialog ? "auto" : "0",
508
+ top: isDialog ? "50%" : "auto",
509
+ right: isClassic || isRight ? "1rem" : isDialog ? "auto" : "0",
510
+ left: isDialog ? "50%" : isClassic || isRight || isFull ? "auto" : "0",
511
+ transform: isDialog ? "translate(-50%, -50%)" : "none",
512
+ width: isFull ? "100%" : isDialog ? "90%" : "24rem",
513
+ height: isClassic
514
+ ? "550px"
515
+ : isDialog
516
+ ? "700px"
517
+ : isFull
518
+ ? "600px"
519
+ : "100%",
520
+ maxWidth: isDialog ? "900px" : "none",
521
+ maxHeight: isDialog ? "90vh" : "none",
522
+ zIndex: isDialog ? 1001 : isClassic || isRight ? 1001 : 50,
523
+ backgroundColor: themeObj().mainBackgroundColor || "white",
301
524
  display: "flex",
302
525
  flexDirection: "column",
303
526
  overflow: "hidden",
304
527
  borderRadius: isFull ? "0" : "0.5rem",
305
528
  border: isFull ? "none" : "1px solid #e5e7eb",
306
529
  boxShadow: isFull ? "none" : "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
530
+ fontFamily: themeObj().fontFamily || undefined,
307
531
  };
308
532
  }
309
533
 
@@ -311,32 +535,33 @@ function ShopPrompter(props) {
311
535
  const pos = props.chatPosition || "classic-chatbot";
312
536
  if (pos === "classic-chatbot") return "sh-classic";
313
537
  if (pos === "right-panel") return "sh-right";
314
- if (pos === "bottom-sheet") return "sh-bottom";
538
+ if (pos === "dialog") {
539
+ return "sh-dialog " + animationClass;
540
+ }
315
541
  return "sh-full";
316
542
  }
317
543
 
318
544
  function messages() {
319
- return chatMessages.map((msg) => ({
320
- ...msg,
321
- safeProductSkus: msg.productSkus || [],
322
- }));
545
+ return chatMessages;
323
546
  }
324
547
 
325
- function showWelcome() {
326
- return chatMessages.length === 0;
548
+ function parseMarkdown(content) {
549
+ if (!content) return "";
550
+ return String(marked.parse(content));
327
551
  }
328
552
 
329
- function getProductImageUrl(sku) {
330
- return "https://placehold.co/100x100?text=" + sku;
553
+ function showWelcome() {
554
+ return chatMessages.length === 0;
331
555
  }
332
556
 
333
- function getProductLink(sku) {
334
- return "/product/" + sku;
557
+ function getShowProducts(message) {
558
+ return message.productSkus && message.productSkus.length > 0;
335
559
  }
336
560
 
337
- function handleSend() {
338
- if (!input.trim() || isLoading) return;
339
- const userQuery = input;
561
+ function handleSend(overrideQuery) {
562
+ const actualQuery = overrideQuery || input;
563
+ if (!actualQuery.trim() || isLoading) return;
564
+ const userQuery = actualQuery;
340
565
  setInput("");
341
566
  const userMsg = {
342
567
  id: Date.now().toString() + "-user",
@@ -344,30 +569,45 @@ function ShopPrompter(props) {
344
569
  content: userQuery,
345
570
  productSkus: [],
346
571
  };
347
- setChatMessages([...chatMessages, userMsg]);
348
572
  const assistantMsg = {
349
573
  id: Date.now().toString() + "-assistant",
350
574
  role: "assistant",
351
575
  content: "",
352
576
  productSkus: [],
353
577
  };
354
- setChatMessages([...chatMessages, assistantMsg]);
578
+ let currentMessages = [...chatMessages, userMsg, assistantMsg];
579
+ setChatMessages(currentMessages);
580
+ setTimeout(() => {
581
+ const inputEl = document.getElementById("shop-prompter-input");
582
+ if (inputEl) {
583
+ inputEl.value = "";
584
+ inputEl.focus();
585
+ }
586
+ }, 0);
355
587
  service?.sendMessage(
356
588
  userQuery,
357
589
  {
358
590
  onTextUpdate: (text) => {
359
- const updatedMessages = [...chatMessages];
591
+ const updatedMessages = [...currentMessages];
360
592
  const lastMsg = updatedMessages[updatedMessages.length - 1];
361
593
  if (lastMsg) {
362
- lastMsg.content = text;
594
+ updatedMessages[updatedMessages.length - 1] = {
595
+ ...lastMsg,
596
+ content: text,
597
+ };
598
+ currentMessages = updatedMessages;
363
599
  setChatMessages(updatedMessages);
364
600
  }
365
601
  },
366
602
  onProductSkus: (skus) => {
367
- const updatedMessages = [...chatMessages];
603
+ const updatedMessages = [...currentMessages];
368
604
  const lastMsg = updatedMessages[updatedMessages.length - 1];
369
605
  if (lastMsg) {
370
- lastMsg.productSkus = skus;
606
+ updatedMessages[updatedMessages.length - 1] = {
607
+ ...lastMsg,
608
+ productSkus: skus,
609
+ };
610
+ currentMessages = updatedMessages;
371
611
  setChatMessages(updatedMessages);
372
612
  }
373
613
  },
@@ -381,16 +621,65 @@ function ShopPrompter(props) {
381
621
  if (props.onCartOperation) {
382
622
  props.onCartOperation(ops);
383
623
  }
624
+ dispatchCartOperation(ops);
384
625
  },
385
626
  },
386
627
  props.cart || []
387
628
  );
388
629
  }
389
630
 
631
+ function dispatchCartOperation(ops) {
632
+ if (typeof window !== "undefined") {
633
+ const root =
634
+ typeof self !== "undefined" &&
635
+ self &&
636
+ "dispatchEvent" in self &&
637
+ !(self instanceof Window)
638
+ ? self
639
+ : null;
640
+ if (root) {
641
+ root.dispatchEvent(
642
+ new CustomEvent("cartOperation", {
643
+ detail: ops,
644
+ })
645
+ );
646
+ }
647
+ }
648
+ }
649
+
390
650
  function toggleChat() {
391
- setShowChat(!showChat);
392
- if (!showChat && props.onClose) {
393
- props.onClose();
651
+ if (showChat) {
652
+ setIsClosing(true);
653
+ setAnimationClass("sh-dialog-animate-out");
654
+ setBackdropAnimationClass("sh-backdrop-animate-out");
655
+ if (closeTimeout) {
656
+ clearTimeout(closeTimeout);
657
+ }
658
+ setCloseTimeout(
659
+ setTimeout(() => {
660
+ setShowChat(false);
661
+ setIsClosing(false);
662
+ setAnimationClass("");
663
+ setBackdropAnimationClass("");
664
+ setCloseTimeout(null);
665
+ if (props.onClose) {
666
+ props.onClose();
667
+ }
668
+ }, 200)
669
+ );
670
+ } else {
671
+ if (closeTimeout) {
672
+ clearTimeout(closeTimeout);
673
+ setCloseTimeout(null);
674
+ }
675
+ setShowChat(true);
676
+ setIsClosing(false);
677
+ setAnimationClass("sh-dialog-animate");
678
+ setBackdropAnimationClass("sh-backdrop-animate");
679
+ setTimeout(() => {
680
+ setAnimationClass("");
681
+ setBackdropAnimationClass("");
682
+ }, 250);
394
683
  }
395
684
  }
396
685
 
@@ -399,16 +688,6 @@ function ShopPrompter(props) {
399
688
  service?.resetSession();
400
689
  }
401
690
 
402
- function onPopularItemsClick() {
403
- setInput(TRANSLATIONS.showPopularItems);
404
- handleSend();
405
- }
406
-
407
- function onCompareProductsClick() {
408
- setInput(TRANSLATIONS.compareProducts);
409
- handleSend();
410
- }
411
-
412
691
  function onInputChange(event) {
413
692
  setInput(event.target.value);
414
693
  }
@@ -419,53 +698,235 @@ function ShopPrompter(props) {
419
698
  }
420
699
  }
421
700
 
422
- function onHeaderButtonMouseEnter() {
423
- setIsHeaderButtonHovered(true);
701
+ function handleTriggerClick(e) {
702
+ if (props.chatPosition !== "dialog") return;
703
+ if (!props.dialogTrigger) return;
704
+ const isTargetClicked =
705
+ typeof props.dialogTrigger === "string"
706
+ ? e.target.closest(props.dialogTrigger)
707
+ : (() => {
708
+ let target = null;
709
+ if (typeof props.dialogTrigger === "object") {
710
+ if ("current" in props.dialogTrigger) {
711
+ target = props.dialogTrigger.current;
712
+ } else {
713
+ target = props.dialogTrigger;
714
+ }
715
+ }
716
+ return target && target.contains && target.contains(e.target);
717
+ })();
718
+ if (isTargetClicked) {
719
+ if (closeTimeout) {
720
+ clearTimeout(closeTimeout);
721
+ setCloseTimeout(null);
722
+ }
723
+ setShowChat(true);
724
+ setIsClosing(false);
725
+ setAnimationClass("sh-dialog-animate");
726
+ setBackdropAnimationClass("sh-backdrop-animate");
727
+ setTimeout(() => {
728
+ setAnimationClass("");
729
+ setBackdropAnimationClass("");
730
+ }, 250);
731
+ }
424
732
  }
425
733
 
426
- function onHeaderButtonMouseLeave() {
427
- setIsHeaderButtonHovered(false);
734
+ function loadCustomFont(fontFamily) {
735
+ if (!fontFamily || typeof window === "undefined") return;
736
+ const systemFonts = [
737
+ "sans-serif",
738
+ "serif",
739
+ "monospace",
740
+ "cursive",
741
+ "fantasy",
742
+ "system-ui",
743
+ "-apple-system",
744
+ "blinkmacsystemfont",
745
+ "segoe ui",
746
+ "roboto",
747
+ "helvetica",
748
+ "arial",
749
+ "inherit",
750
+ ];
751
+ const cleanFont = fontFamily
752
+ .split(",")[0]
753
+ .trim()
754
+ .replace(new RegExp("['\"]", "g"), "");
755
+ if (!cleanFont || systemFonts.includes(cleanFont.toLowerCase())) return;
756
+ const linkId = `shopprompter-font-${cleanFont
757
+ .toLowerCase()
758
+ .replace(new RegExp("\\s+", "g"), "-")}`;
759
+ if (document.getElementById(linkId)) return;
760
+ const link = document.createElement("link");
761
+ link.id = linkId;
762
+ link.rel = "stylesheet";
763
+ const fontNameUrl = cleanFont.replace(new RegExp("\\s+", "g"), "+");
764
+ link.href = `https://fonts.googleapis.com/css2?family=${fontNameUrl}:wght@400;500;600;700&display=swap`;
765
+ document.head.appendChild(link);
428
766
  }
429
767
 
430
- function doSomethingAsync() {
768
+ function loadAgentExperience() {
431
769
  void (async function () {
432
- console.log("## ~ ShopPrompter ~ state.service:", service);
433
770
  if (!service) {
434
- console.warn("Service not initialized yet");
771
+ console.warn(
772
+ "Service not initialized yet! Cannot load AgentExperience."
773
+ );
435
774
  return;
436
775
  }
437
776
  const response = await service?.getAgentExperience();
438
- console.log("## ~ ShopPrompter ~ response:", response);
439
777
  setAgentExperience(response);
440
778
  })();
441
779
  }
442
780
 
781
+ function handleFeedback(messageId, messageContent, newFeedback) {
782
+ const msg = chatMessages.find((m) => m.id === messageId);
783
+ if (!msg) return;
784
+ const currentFeedback = msg.feedback;
785
+ const targetFeedback =
786
+ currentFeedback === newFeedback ? "clear" : newFeedback;
787
+
788
+ // Optimistically update the UI state
789
+ const oldMessages = [...chatMessages];
790
+ setChatMessages(
791
+ chatMessages.map((m) => {
792
+ if (m.id === messageId) {
793
+ return {
794
+ ...m,
795
+ feedback: targetFeedback === "clear" ? null : targetFeedback,
796
+ };
797
+ }
798
+ return m;
799
+ })
800
+ );
801
+ setError(null);
802
+ service
803
+ ?.sendFeedback(messageId, messageContent, targetFeedback)
804
+ .catch((err) => {
805
+ // Revert optimistic update
806
+ setChatMessages(oldMessages);
807
+ setError(err?.message || TRANSLATIONS.feedbackError);
808
+ });
809
+ }
810
+
811
+ function canShowFeedback(message) {
812
+ if (message.role !== "assistant") return false;
813
+ if (!message.content) return false;
814
+ const lastMsg = chatMessages[chatMessages.length - 1];
815
+ if (lastMsg && lastMsg.id === message.id && isLoading) {
816
+ return false;
817
+ }
818
+ return true;
819
+ }
820
+
443
821
  useEffect(() => {
444
- const shopPrompterService = new ShopPrompterService();
445
- setService(shopPrompterService);
822
+ if (
823
+ typeof window !== "undefined" &&
824
+ !document.getElementById("lottie-player-script")
825
+ ) {
826
+ const script = document.createElement("script");
827
+ script.id = "lottie-player-script";
828
+ script.src =
829
+ "https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player";
830
+ document.head.appendChild(script);
831
+ }
832
+ if (themeObj().fontFamily) {
833
+ loadCustomFont(themeObj().fontFamily);
834
+ }
835
+ if (apiConfigObj()) {
836
+ if (!isConfigInsufficient()) {
837
+ const apiConfig = {
838
+ ...apiConfigObj(),
839
+ shopPrompterToken:
840
+ props.shopPrompterToken || apiConfigObj().shopPrompterToken,
841
+ };
842
+ const service = new ShopPrompterService(apiConfig);
843
+ setService(service);
844
+ // Restore persisted messages for the current session.
845
+ const savedMessages = service.loadMessages();
846
+ if (savedMessages.length > 0) {
847
+ setChatMessages(savedMessages);
848
+ }
849
+ }
850
+ } else {
851
+ console.error("ShopPrompter: apiConfig prop is required!");
852
+ }
853
+ if (
854
+ props?.chatPosition === "right-panel" ||
855
+ props?.chatPosition === "full-page"
856
+ ) {
857
+ setShowChat(true);
858
+ }
859
+ if (typeof window !== "undefined") {
860
+ document.addEventListener("click", handleTriggerClick);
861
+ }
446
862
  }, []);
447
863
  useEffect(() => {
448
864
  if (service && !agentExperience) {
449
- doSomethingAsync();
450
- }
451
- if (agentExperience) {
452
- console.log("## ~ ShopPrompter ~ agentExperience updated:", {
453
- greeting: agentExperience.greeting,
454
- agentLogoUrl: agentExperience.agentLogoUrl,
455
- });
865
+ loadAgentExperience();
456
866
  }
457
867
  }, [service, agentExperience]);
868
+ useEffect(() => {
869
+ if (themeObj().fontFamily) {
870
+ loadCustomFont(themeObj().fontFamily);
871
+ }
872
+ }, [themeObj().fontFamily || ""]);
873
+ useEffect(() => {
874
+ if (
875
+ props?.chatPosition === "right-panel" ||
876
+ props?.chatPosition === "full-page"
877
+ ) {
878
+ setShowChat(true);
879
+ } else if (
880
+ props?.chatPosition === "dialog" ||
881
+ props?.chatPosition === "classic-chatbot"
882
+ ) {
883
+ setShowChat(false);
884
+ setIsClosing(false);
885
+ setAnimationClass("");
886
+ setBackdropAnimationClass("");
887
+ }
888
+ }, [props?.chatPosition || ""]);
458
889
 
459
890
  return (
460
891
  <div className="shopprompter-sdk">
892
+ <style dangerouslySetInnerHTML={{ __html: ANIMATION_STYLES }} />
893
+ <div
894
+ onClick={(e) => toggleChat()}
895
+ style={{
896
+ ...styles.backdrop,
897
+ display:
898
+ props.chatPosition === "dialog" && (showChat || isClosing)
899
+ ? "block"
900
+ : "none",
901
+ }}
902
+ className={backdropAnimationClass}
903
+ />
461
904
  {props.chatPosition === "classic-chatbot" ? (
462
905
  <>
463
906
  {!showChat ? (
464
907
  <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
908
  onClick={(e) => toggleChat()}
467
909
  style={{
468
- boxShadow: "0 0 0 8px rgba(37, 99, 235, 0.2)",
910
+ position: "fixed",
911
+ bottom: "1.5rem",
912
+ right: "1.5rem",
913
+ width: "4rem",
914
+ height: "4rem",
915
+ backgroundColor: themeObj().actionColor || "#2563eb",
916
+ borderRadius: "9999px",
917
+ zIndex: 50,
918
+ display: "flex",
919
+ alignItems: "center",
920
+ justifyContent: "center",
921
+ transition: "background-color 0.2s",
922
+ border: "none",
923
+ cursor: "pointer",
924
+ boxShadow: themeObj().actionColor
925
+ ? `0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05), 0 0 0 8px ${
926
+ themeObj().actionColor
927
+ }33`
928
+ : "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)",
929
+ fontFamily: themeObj().fontFamily || undefined,
469
930
  }}
470
931
  >
471
932
  <img
@@ -480,107 +941,56 @@ function ShopPrompter(props) {
480
941
  ) : null}
481
942
  </>
482
943
  ) : null}
483
- {showChat ? (
944
+ {showChat || isClosing ? (
484
945
  <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>
946
+ <div
947
+ style={{
948
+ ...styles.header,
949
+ backgroundColor:
950
+ themeObj().headerBackgroundColor || "transparent",
951
+ }}
952
+ >
953
+ {themeObj().showAgentName !== false ? (
954
+ <div style={styles.subHeaderContainer}>
955
+ {agentExperience?.agentLogoUrl ? (
956
+ <img
957
+ src={agentExperience.agentLogoUrl}
958
+ alt={agentExperience.agentName}
959
+ style={{
960
+ width: "24px",
961
+ height: "24px",
962
+ borderRadius: "4px",
963
+ objectFit: "cover",
964
+ }}
965
+ />
966
+ ) : null}
967
+ {agentExperience?.agentName ? (
968
+ <span style={styles.headerText}>
969
+ {agentExperience.agentName}
970
+ </span>
971
+ ) : null}
972
+ </div>
973
+ ) : (
974
+ <>
975
+ <div style={styles.subHeaderContainer} />
976
+ </>
977
+ )}
978
+ {!hasToken() && apiConfigObj?.()?.apiKey ? (
979
+ <div style={styles.warningBanner}>
980
+ API Key is only for development purposes
981
+ </div>
982
+ ) : null}
569
983
  <div style={styles.subHeaderContainer}>
570
984
  <button
985
+ className="sh-header-btn"
571
986
  onClick={(e) => handleNewChat()}
572
- onMouseEnter={(e) => onHeaderButtonMouseEnter()}
573
- onMouseLeave={(e) => onHeaderButtonMouseLeave()}
574
- style={{
575
- ...styles.headerButton,
576
- ...(isHeaderButtonHovered ? styles.headerButton.onHover : {}),
577
- }}
987
+ style={styles.headerButton}
578
988
  >
579
989
  {TRANSLATIONS.newChat}
580
990
  </button>
581
991
  {props.chatPosition === "classic-chatbot" ||
582
992
  props.chatPosition === "right-panel" ||
583
- props.chatPosition === "bottom-sheet" ? (
993
+ props.chatPosition === "dialog" ? (
584
994
  <button
585
995
  onClick={(e) => toggleChat()}
586
996
  style={styles.ButtonSheetButton}
@@ -590,135 +1000,349 @@ function ShopPrompter(props) {
590
1000
  ) : null}
591
1001
  </div>
592
1002
  </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
- }
1003
+ {isConfigInsufficient() ? (
1004
+ <div style={styles.insufficientConfigContainer}>
1005
+ <div style={styles.insufficientConfigIconContainer}>
1006
+ <svg
1007
+ width="48"
1008
+ height="48"
1009
+ viewBox="0 0 24 24"
1010
+ fill="none"
1011
+ stroke="currentColor"
1012
+ strokeWidth="2"
1013
+ strokeLinecap="round"
1014
+ strokeLinejoin="round"
603
1015
  style={{
604
- width: "64px",
605
- height: "64px",
606
- marginBottom: "24px",
1016
+ color: "#ef4444",
607
1017
  }}
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()}
1018
+ >
1019
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
1020
+ <path d="M7 11V7a5 5 0 0 1 9.9-1" />
1021
+ </svg>
1022
+ </div>
1023
+ <h3
1024
+ style={{
1025
+ ...styles.insufficientConfigTitle,
1026
+ fontFamily: themeObj().fontFamily || undefined,
1027
+ }}
1028
+ >
1029
+ Configuration Error
1030
+ </h3>
1031
+ <p
1032
+ style={{
1033
+ ...styles.insufficientConfigMessage,
1034
+ fontFamily: themeObj().fontFamily || undefined,
1035
+ }}
1036
+ >
1037
+ ShopPrompter requires a secure token or API key to initialize.
1038
+ Please check your setup.
1039
+ </p>
1040
+ <div
1041
+ style={{
1042
+ ...styles.insufficientConfigDetails,
1043
+ fontFamily: themeObj().fontFamily || undefined,
1044
+ }}
1045
+ >
1046
+ <div
1047
+ style={{
1048
+ fontWeight: 600,
1049
+ marginBottom: "0.25rem",
1050
+ }}
1051
+ >
1052
+ Missing Parameters:
1053
+ </div>
1054
+ <div
1055
+ style={{
1056
+ display: "flex",
1057
+ flexDirection: "column",
1058
+ gap: "0.25rem",
1059
+ fontFamily: "monospace",
1060
+ fontSize: "0.75rem",
1061
+ }}
1062
+ >
1063
+ <span
1064
+ style={{
1065
+ color: "#ef4444",
1066
+ }}
621
1067
  >
622
- {TRANSLATIONS.showPopularItems}
623
- </button>
624
- <button
625
- style={styles.button}
626
- onClick={(e) => onCompareProductsClick()}
1068
+ ✗ shopPrompterToken (not found)
1069
+ </span>
1070
+ <span
1071
+ style={{
1072
+ color: "#ef4444",
1073
+ }}
627
1074
  >
628
- {TRANSLATIONS.compareProducts}
629
- </button>
1075
+ ✗ apiConfig.apiKey (not found)
1076
+ </span>
630
1077
  </div>
631
1078
  </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
1079
+ </div>
1080
+ ) : null}
1081
+ {!isConfigInsufficient() ? (
1082
+ <div
1083
+ style={{
1084
+ display: "flex",
1085
+ flexDirection: "column",
1086
+ flex: 1,
1087
+ overflow: "hidden",
1088
+ }}
1089
+ >
1090
+ <div
1091
+ id="shop-prompter-chat-container"
1092
+ style={{
1093
+ ...styles.chatContainer,
1094
+ backgroundColor:
1095
+ themeObj().mainBackgroundColor ||
1096
+ styles.chatContainer.backgroundColor,
1097
+ }}
1098
+ >
1099
+ {showWelcome() ? (
1100
+ <div style={styles.welcomeContainer}>
1101
+ <img
1102
+ alt="ShopPrompter"
1103
+ src={
1104
+ agentExperience?.agentLogoUrl
1105
+ ? agentExperience.agentLogoUrl
1106
+ : ICONS.shopprompter
648
1107
  }
1108
+ style={{
1109
+ width: "64px",
1110
+ height: "64px",
1111
+ marginBottom: "24px",
1112
+ borderRadius: "12px",
1113
+ objectFit: "cover",
1114
+ }}
1115
+ />
1116
+ <h3
1117
+ style={{
1118
+ ...styles.welcomeTitle,
1119
+ fontFamily:
1120
+ themeObj().fontFamily ||
1121
+ styles.welcomeTitle.fontFamily,
1122
+ }}
649
1123
  >
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}
1124
+ {agentExperience?.greeting?.title ||
1125
+ TRANSLATIONS.welcomeToShopPrompter}
1126
+ </h3>
1127
+ <p
1128
+ style={{
1129
+ ...styles.welcomeDescription,
1130
+ fontFamily:
1131
+ themeObj().fontFamily ||
1132
+ styles.welcomeDescription.fontFamily,
1133
+ }}
1134
+ >
1135
+ {agentExperience?.greeting?.description ||
1136
+ TRANSLATIONS.welcomeDescription}
1137
+ </p>
1138
+ <div style={styles.buttonContainer}>
1139
+ {prompts()?.map((item) => (
1140
+ <button
1141
+ key={item.prompt}
1142
+ style={styles.button}
1143
+ onClick={(event) => {
1144
+ setInput(item.prompt);
1145
+ handleSend(item.prompt);
1146
+ }}
1147
+ >
1148
+ {item.prompt}
1149
+ </button>
1150
+ ))}
664
1151
  </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%",
1152
+ </div>
1153
+ ) : null}
1154
+ {!showWelcome() ? (
1155
+ <div
1156
+ style={{
1157
+ width: "100%",
1158
+ }}
1159
+ >
1160
+ {chatMessages?.map((message) => (
1161
+ <div key={message.id}>
1162
+ {message.role === "user" ? (
1163
+ <div style={styles.messageContainerUser}>
1164
+ <div
1165
+ style={{
1166
+ ...styles.messageBubbleUser,
1167
+ backgroundColor:
1168
+ themeObj().userMessageBackgroundColor ||
1169
+ styles.messageBubbleUser.backgroundColor,
1170
+ }}
1171
+ >
1172
+ {message.content}
1173
+ </div>
1174
+ </div>
1175
+ ) : null}
1176
+ {message.role === "assistant" ? (
1177
+ <div style={styles.messageContainerAssistant}>
1178
+ <div
1179
+ style={{
1180
+ ...styles.messageBubbleAssistant,
1181
+ backgroundColor:
1182
+ themeObj().assistantMessageBackgroundColor ||
1183
+ styles.messageBubbleAssistant.backgroundColor,
1184
+ }}
1185
+ >
1186
+ <div
1187
+ dangerouslySetInnerHTML={{
1188
+ __html: parseMarkdown(message.content),
676
1189
  }}
677
1190
  />
1191
+ {!message.content && isLoading ? (
1192
+ <>
1193
+ {agentExperience?.loadingIndicatorUrl ? (
1194
+ <lottie-player
1195
+ background="transparent"
1196
+ speed="1"
1197
+ src={agentExperience.loadingIndicatorUrl}
1198
+ style={{
1199
+ width: "40px",
1200
+ height: "40px",
1201
+ }}
1202
+ loop
1203
+ autoplay
1204
+ />
1205
+ ) : (
1206
+ <div style={styles.thinkingIndicator}>
1207
+ <svg
1208
+ width="24"
1209
+ height="24"
1210
+ viewBox="0 0 24 24"
1211
+ fill="none"
1212
+ stroke="currentColor"
1213
+ strokeWidth="2"
1214
+ strokeLinecap="round"
1215
+ strokeLinejoin="round"
1216
+ style={{
1217
+ color: "#6b7280",
1218
+ }}
1219
+ >
1220
+ <path d={THINKING_SPINNER} />
1221
+ <animateTransform
1222
+ attributeName="transform"
1223
+ type="rotate"
1224
+ from="0 12 12"
1225
+ to="360 12 12"
1226
+ dur="1s"
1227
+ repeatCount="indefinite"
1228
+ />
1229
+ </svg>
1230
+ </div>
1231
+ )}
1232
+ </>
1233
+ ) : null}
678
1234
  </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>
1235
+ {canShowFeedback(message) ? (
1236
+ <div style={styles.feedbackRow}>
1237
+ <button
1238
+ className="sh-feedback-btn"
1239
+ onClick={(event) =>
1240
+ handleFeedback(
1241
+ message.id,
1242
+ message.content,
1243
+ "upvote"
1244
+ )
1245
+ }
1246
+ style={styles.feedbackButton}
1247
+ >
1248
+ <ThumbsUpIcon
1249
+ fill={
1250
+ message.feedback === "upvote"
1251
+ ? themeObj().actionColor || "#2563eb"
1252
+ : "none"
1253
+ }
1254
+ stroke={
1255
+ message.feedback === "upvote"
1256
+ ? themeObj().actionColor || "#2563eb"
1257
+ : "currentColor"
1258
+ }
1259
+ />
1260
+ </button>
1261
+ <button
1262
+ className="sh-feedback-btn"
1263
+ onClick={(event) =>
1264
+ handleFeedback(
1265
+ message.id,
1266
+ message.content,
1267
+ "downvote"
1268
+ )
1269
+ }
1270
+ style={styles.feedbackButton}
1271
+ >
1272
+ <ThumbsDownIcon
1273
+ fill={
1274
+ message.feedback === "downvote"
1275
+ ? themeObj().actionColor || "#2563eb"
1276
+ : "none"
1277
+ }
1278
+ stroke={
1279
+ message.feedback === "downvote"
1280
+ ? themeObj().actionColor || "#2563eb"
1281
+ : "currentColor"
1282
+ }
1283
+ />
1284
+ </button>
1285
+ </div>
1286
+ ) : null}
1287
+ {getShowProducts(message) ? (
1288
+ <ProductCardList
1289
+ skus={message.productSkus}
1290
+ defaultLocale={props.defaultLocale}
1291
+ />
1292
+ ) : null}
688
1293
  </div>
689
- ))}
1294
+ ) : null}
690
1295
  </div>
691
- ) : null}
1296
+ ))}
692
1297
  </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()}
1298
+ ) : null}
1299
+ {error ? (
1300
+ <div style={styles.errorContainer}>Error: {error}</div>
1301
+ ) : null}
1302
+ </div>
1303
+ <div
712
1304
  style={{
713
- ...styles.formButton,
714
- ...styles.sendButton,
1305
+ ...styles.inputSection,
1306
+ backgroundColor:
1307
+ themeObj().mainBackgroundColor ||
1308
+ styles.inputSection.backgroundColor,
715
1309
  }}
716
1310
  >
717
- <img alt="Send" src={ICONS.sendHorizontal} />
718
- </button>
1311
+ <div style={styles.inputWrapper}>
1312
+ <input
1313
+ id="shop-prompter-input"
1314
+ type="text"
1315
+ style={styles.input}
1316
+ placeholder={
1317
+ agentExperience?.hintText || TRANSLATIONS.askMeAnything
1318
+ }
1319
+ value={input}
1320
+ onInput={(e) => onInputChange(e)}
1321
+ onKeyDown={(e) => onInputKeyDown(e)}
1322
+ />
1323
+ <button
1324
+ onClick={(e) => handleSend()}
1325
+ style={{
1326
+ ...styles.formButton,
1327
+ ...styles.sendButton,
1328
+ backgroundColor:
1329
+ themeObj().actionColor ||
1330
+ styles.sendButton.backgroundColor,
1331
+ }}
1332
+ >
1333
+ <img
1334
+ alt="Send"
1335
+ src={ICONS.sendHorizontal}
1336
+ style={{
1337
+ padding: "4px",
1338
+ }}
1339
+ />
1340
+ </button>
1341
+ </div>
1342
+ <p style={styles.disclaimer}>AI content may be inaccurate.</p>
1343
+ </div>
719
1344
  </div>
720
- <p style={styles.disclaimer}>AI content may be inaccurate.</p>
721
- </div>
1345
+ ) : null}
722
1346
  </div>
723
1347
  ) : null}
724
1348
  </div>