@blotoutio/providers-shop-gpt-sdk 1.7.1 → 1.9.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.
Files changed (4) hide show
  1. package/index.cjs.js +400 -101
  2. package/index.js +400 -101
  3. package/index.mjs +400 -101
  4. package/package.json +1 -1
package/index.mjs CHANGED
@@ -362,6 +362,50 @@ const usStates = new Map([
362
362
  ]);
363
363
  new Set([...isoCountries.keys(), ...usStates.keys()]);
364
364
 
365
+ const createEnabled = () => ({
366
+ name: 'enabled',
367
+ groupNames: new Set(),
368
+ groupName: '',
369
+ isEnabled: true,
370
+ });
371
+ const createDisabled = () => ({
372
+ name: 'disabled',
373
+ groupNames: new Set(),
374
+ groupName: '',
375
+ isEnabled: false,
376
+ });
377
+ const createABTest = ({ userId }) => {
378
+ const [sample] = userId.split('-');
379
+ const segment = parseInt(sample, 16) % 2;
380
+ return {
381
+ name: 'ab-test',
382
+ groupNames: new Set(['enabled', 'control']),
383
+ groupName: segment == 1 ? 'enabled' : 'control',
384
+ isEnabled: segment == 1,
385
+ };
386
+ };
387
+ const createPreview = ({ preview }) => {
388
+ return {
389
+ name: 'preview',
390
+ groupNames: new Set(['preview']),
391
+ groupName: preview ? 'preview' : '',
392
+ isEnabled: preview !== null && preview !== void 0 ? preview : false,
393
+ };
394
+ };
395
+ const getExperimentName = (mode) => (mode !== null && mode !== void 0 ? mode : 'disabled');
396
+ const createExperiment = (props) => {
397
+ switch (props.name) {
398
+ case 'enabled':
399
+ return createEnabled();
400
+ case 'disabled':
401
+ return createDisabled();
402
+ case 'ab-test':
403
+ return createABTest(props);
404
+ case 'preview':
405
+ return createPreview(props);
406
+ }
407
+ };
408
+
365
409
  const packageName = 'shopGPT';
366
410
  const DEFAULT_MAX_THREAD_AGE = 14;
367
411
  const previewKeyName = 'previewShopGPT';
@@ -426,7 +470,6 @@ const createShopGPTAPI = ({ fetch: fetchImpl = window.fetch, baseURL, userId, st
426
470
  return url;
427
471
  };
428
472
  const processQuery = async (query, threadId, productHandle) => {
429
- var _a;
430
473
  const response = await fetchImpl(getURL('/query'), {
431
474
  method: 'POST',
432
475
  headers: getHeaders(),
@@ -441,14 +484,7 @@ const createShopGPTAPI = ({ fetch: fetchImpl = window.fetch, baseURL, userId, st
441
484
  if (!response.ok) {
442
485
  throw new Error(`Failed to process the query - ${response.status}: ${await response.text()}`);
443
486
  }
444
- const data = (await response.json());
445
- return {
446
- messageId: data.messageId,
447
- message: data.message,
448
- products: (_a = data.products) === null || _a === void 0 ? void 0 : _a.filter((item) => !!item).map((item) => ({ ...item, quantity: 1 })),
449
- chatTitle: data.chatTitle,
450
- welcomePrompts: data.welcomePrompts,
451
- };
487
+ return response;
452
488
  };
453
489
  const fetchChatHistory = async (threadId) => {
454
490
  if (!threadId) {
@@ -528,6 +564,18 @@ const createShopGPTAPI = ({ fetch: fetchImpl = window.fetch, baseURL, userId, st
528
564
  throw new Error(`Failed to save feedback - ${response.status}: ${await response.text()}`);
529
565
  }
530
566
  };
567
+ const fetchCustomPrompts = async (threadId) => {
568
+ const response = await fetchImpl(getURL(`/custom-prompts?threadId=${threadId}`), {
569
+ method: 'GET',
570
+ headers: getHeaders(),
571
+ credentials: 'include',
572
+ });
573
+ if (!response.ok) {
574
+ throw new Error(`Could not fetch custom prompts - ${response.status}: ${await response.text()}`);
575
+ }
576
+ const data = (await response.json());
577
+ return data.customPrompts;
578
+ };
531
579
  return {
532
580
  processQuery,
533
581
  fetchChatHistory,
@@ -536,6 +584,7 @@ const createShopGPTAPI = ({ fetch: fetchImpl = window.fetch, baseURL, userId, st
536
584
  deleteSingleThread,
537
585
  deleteAllThreads,
538
586
  saveFeedback,
587
+ fetchCustomPrompts,
539
588
  };
540
589
  };
541
590
 
@@ -569,11 +618,15 @@ const init = (params) => {
569
618
  // exit if not in top window
570
619
  return;
571
620
  }
572
- const { enabled, devMode, merchantUrl, profiles, productHandles, targetPath, uiMode, brandName, quickPrompts, merchantImage, latestThreadLoad, } = (_c = params.manifest.variables) !== null && _c !== void 0 ? _c : {};
573
- let shouldShowUI = enabled;
574
- if (!enabled && hasPreviewKey()) {
621
+ const { enabled, mode, devMode, merchantUrl, profiles, productHandles, targetPath, view, brandName, quickPrompts, merchantImage, latestThreadLoad, botIconUrl, css, } = (_c = params.manifest.variables) !== null && _c !== void 0 ? _c : {};
622
+ const experiment = createExperiment({
623
+ name: getExperimentName(mode),
624
+ userId: params.userId,
625
+ preview: hasPreviewKey(),
626
+ });
627
+ const shouldShowUI = enabled || experiment.isEnabled;
628
+ if (experiment.name === 'preview' && shouldShowUI) {
575
629
  logger.log('Enabling UI in preview mode');
576
- shouldShowUI = true;
577
630
  }
578
631
  if (shouldShowUI) {
579
632
  const uiImplementation = window[registryKey].ui;
@@ -590,7 +643,7 @@ const init = (params) => {
590
643
  storeAPI,
591
644
  shopGPTAPI,
592
645
  devMode,
593
- uiMode,
646
+ view,
594
647
  merchantUrl,
595
648
  profiles,
596
649
  productHandles,
@@ -599,6 +652,8 @@ const init = (params) => {
599
652
  quickPrompts,
600
653
  merchantImage,
601
654
  latestThreadLoad: latestThreadLoad !== null && latestThreadLoad !== void 0 ? latestThreadLoad : DEFAULT_MAX_THREAD_AGE,
655
+ botIconUrl,
656
+ css,
602
657
  });
603
658
  }
604
659
  };
@@ -725,6 +780,14 @@ const scrollBarStyles = i$4 `
725
780
  const shopGPTStyles = i$4 `
726
781
  ${scrollBarStyles}
727
782
 
783
+ :host {
784
+ --shopgpt-primary: #001531;
785
+ --shopgpt-secondary: #172a41;
786
+ --shopgpt-white: #ffffff;
787
+ --shopgpt-border: #dbe2eb;
788
+ --shopgpt-warning: #ffcc81;
789
+ }
790
+
728
791
  * {
729
792
  box-sizing: border-box;
730
793
  }
@@ -757,6 +820,7 @@ const shopGPTStyles = i$4 `
757
820
  0px 20px 20px 0px rgba(0, 0, 0, 0.08);
758
821
  position: fixed;
759
822
  z-index: 2000;
823
+ background: var(--shopgpt-white);
760
824
 
761
825
  @media screen and (max-width: 768px) {
762
826
  min-height: unset;
@@ -775,21 +839,21 @@ const shopGPTStyles = i$4 `
775
839
  z-index: 1000;
776
840
 
777
841
  button {
778
- color: #ffffff;
842
+ color: var(--shopgpt-white);
779
843
  border: none;
780
844
  cursor: pointer;
781
845
  width: 56px;
782
846
  height: 56px;
783
- background-color: #001531;
847
+ background-color: var(--shopgpt-primary);
784
848
  border-radius: 50%;
785
849
  justify-content: center;
786
850
  align-items: center;
787
- box-shadow: 0 0 4px 1px #ffffff;
851
+ box-shadow: 0 0 4px 1px var(--shopgpt-white);
788
852
  }
789
853
 
790
854
  .chatbot-hover-text {
791
855
  position: absolute;
792
- color: #172a41;
856
+ color: var(--shopgpt-secondary);
793
857
  padding: 8px;
794
858
  white-space: nowrap;
795
859
  font-size: 16px;
@@ -802,7 +866,7 @@ const shopGPTStyles = i$4 `
802
866
  right: calc(100% + 5px);
803
867
 
804
868
  border-radius: 5px 5px 0px;
805
- background: #ffcc81;
869
+ background: var(--shopgpt-warning);
806
870
  box-shadow: 0px 4px 6px -1px rgba(0, 0, 0, 0.1),
807
871
  0px 2px 4px -1px rgba(0, 0, 0, 0.06);
808
872
 
@@ -824,7 +888,7 @@ const shopGPTStyles = i$4 `
824
888
  display: flex;
825
889
  overflow: hidden;
826
890
  height: 100%;
827
- background: #ffffff;
891
+ background: var(--shopgpt-white);
828
892
  justify-content: center;
829
893
  align-items: center;
830
894
  padding: 25px;
@@ -835,7 +899,7 @@ const shopGPTStyles = i$4 `
835
899
  .shopgpt-container {
836
900
  display: flex;
837
901
  gap: 1px;
838
- background: #dbe2eb;
902
+ background: var(--shopgpt-border);
839
903
  height: 100%;
840
904
  width: 100%;
841
905
 
@@ -1010,6 +1074,10 @@ const chatThreadsStyles = i$4 `
1010
1074
  &:hover {
1011
1075
  background: #091627;
1012
1076
  }
1077
+
1078
+ &:disabled {
1079
+ cursor: not-allowed;
1080
+ }
1013
1081
  }
1014
1082
 
1015
1083
  .history {
@@ -1158,6 +1226,16 @@ class ChatThreads extends r$2 {
1158
1226
  constructor() {
1159
1227
  super(...arguments);
1160
1228
  this.deleteAllThreads = false;
1229
+ this.isStylesheetInjected = false;
1230
+ }
1231
+ connectedCallback() {
1232
+ super.connectedCallback();
1233
+ if (!this.isStylesheetInjected && this.css) {
1234
+ const sheet = new CSSStyleSheet();
1235
+ sheet.replaceSync(this.css);
1236
+ this.shadowRoot.adoptedStyleSheets.push(sheet);
1237
+ this.isStylesheetInjected = true;
1238
+ }
1161
1239
  }
1162
1240
  getDomain() {
1163
1241
  var _a;
@@ -1255,6 +1333,7 @@ class ChatThreads extends r$2 {
1255
1333
  <span class="line"></span>
1256
1334
  <button
1257
1335
  class="btn-new-search"
1336
+ ?disabled=${this.isLoading || this.isTyping}
1258
1337
  @click=${() => this.setSelectedThreadId('')}
1259
1338
  >
1260
1339
  New Search
@@ -1263,7 +1342,7 @@ class ChatThreads extends r$2 {
1263
1342
  ${this.merchantUrl
1264
1343
  ? x `<div class="footer">
1265
1344
  Catalog:
1266
- <a href="${this.getDomain()}" target="_blank">
1345
+ <a href="${this.getDomain()}" target="_blank" rel="noopener">
1267
1346
  ${this.getDomain().replace('https://', '')}
1268
1347
  </a>
1269
1348
  </div>`
@@ -1325,6 +1404,10 @@ __decorate([
1325
1404
  r(),
1326
1405
  __metadata("design:type", Object)
1327
1406
  ], ChatThreads.prototype, "deleteAllThreads", void 0);
1407
+ __decorate([
1408
+ n({ type: String }),
1409
+ __metadata("design:type", String)
1410
+ ], ChatThreads.prototype, "css", void 0);
1328
1411
  if (!customElements.get('chat-threads')) {
1329
1412
  customElements.define('chat-threads', ChatThreads);
1330
1413
  }
@@ -1763,8 +1846,18 @@ if (!customElements.get('products-list')) {
1763
1846
  }
1764
1847
 
1765
1848
  class ProductsSection extends r$2 {
1849
+ constructor() {
1850
+ super(...arguments);
1851
+ this.isStylesheetInjected = false;
1852
+ }
1766
1853
  connectedCallback() {
1767
1854
  super.connectedCallback();
1855
+ if (!this.isStylesheetInjected && this.css) {
1856
+ const sheet = new CSSStyleSheet();
1857
+ sheet.replaceSync(this.css);
1858
+ this.shadowRoot.adoptedStyleSheets.push(sheet);
1859
+ this.isStylesheetInjected = true;
1860
+ }
1768
1861
  }
1769
1862
  renderMerchantImage() {
1770
1863
  if (this.merchantImage) {
@@ -1841,6 +1934,10 @@ __decorate([
1841
1934
  e$3('.products'),
1842
1935
  __metadata("design:type", Object)
1843
1936
  ], ProductsSection.prototype, "productsEle", void 0);
1937
+ __decorate([
1938
+ n({ type: String }),
1939
+ __metadata("design:type", String)
1940
+ ], ProductsSection.prototype, "css", void 0);
1844
1941
  if (!customElements.get('products-section')) {
1845
1942
  customElements.define('products-section', ProductsSection);
1846
1943
  }
@@ -1957,8 +2054,8 @@ const chatSectionStyles = i$4 `
1957
2054
  }
1958
2055
 
1959
2056
  .chatbot-section.modal-view {
1960
- height: calc(100% - 121px);
1961
- padding: 0px 16px 44px;
2057
+ height: calc(100% - 93px);
2058
+ padding: 0px 16px 16px;
1962
2059
  gap: 16px;
1963
2060
  }
1964
2061
 
@@ -2107,6 +2204,9 @@ const chatSectionStyles = i$4 `
2107
2204
  border-radius: 5px;
2108
2205
  border: 1px solid #dbe2eb;
2109
2206
  background: #fff;
2207
+ width: 36px;
2208
+ height: 36px;
2209
+ box-sizing: border-box;
2110
2210
  }
2111
2211
 
2112
2212
  .line {
@@ -2234,6 +2334,7 @@ const chatSectionStyles = i$4 `
2234
2334
  padding: 10px;
2235
2335
  border-radius: 10px;
2236
2336
  border: 1px solid #a3b2c6;
2337
+ text-decoration: none;
2237
2338
 
2238
2339
  color: #4e647f;
2239
2340
  line-height: 21px;
@@ -2342,6 +2443,17 @@ const chatSectionStyles = i$4 `
2342
2443
  }
2343
2444
  }
2344
2445
 
2446
+ footer {
2447
+ text-align: center;
2448
+ font-size: 10px;
2449
+ color: #4e647f;
2450
+ font-weight: 500;
2451
+
2452
+ a {
2453
+ text-decoration: none;
2454
+ }
2455
+ }
2456
+
2345
2457
  ${scrollBarStyles}
2346
2458
  `;
2347
2459
 
@@ -2985,7 +3097,7 @@ const markdown = (text) => {
2985
3097
  stash[--si] = p4
2986
3098
  ? p2
2987
3099
  ? `<img src="${p4}" alt="${p3}"/>`
2988
- : `<a href="${p4}">${highlight(p3)}</a>`
3100
+ : `<a href="${p4}" rel="noopener">${highlight(p3)}</a>`
2989
3101
  : p6;
2990
3102
  return `${si}\uf8ff`;
2991
3103
  });
@@ -3011,12 +3123,12 @@ const markdown = (text) => {
3011
3123
  };
3012
3124
 
3013
3125
  class MarkdownRenderer extends r$2 {
3126
+ constructor() {
3127
+ super(...arguments);
3128
+ this.content = '';
3129
+ }
3014
3130
  render() {
3015
- // Remove lit markers from slot content
3016
- const slotContent = this.innerHTML
3017
- .trim()
3018
- .replace(/<!--\?lit\$[\d$]+-->/g, '');
3019
- return o(markdown(slotContent));
3131
+ return o(markdown(this.content));
3020
3132
  }
3021
3133
  }
3022
3134
  MarkdownRenderer.styles = i$4 `
@@ -3030,6 +3142,10 @@ MarkdownRenderer.styles = i$4 `
3030
3142
  }
3031
3143
  }
3032
3144
  `;
3145
+ __decorate([
3146
+ n({ type: String }),
3147
+ __metadata("design:type", Object)
3148
+ ], MarkdownRenderer.prototype, "content", void 0);
3033
3149
  if (!customElements.get('markdown-renderer')) {
3034
3150
  customElements.define('markdown-renderer', MarkdownRenderer);
3035
3151
  }
@@ -3485,6 +3601,16 @@ class ChatSection extends r$2 {
3485
3601
  this.showChatThreads = false;
3486
3602
  this.deleteAllThreads = false;
3487
3603
  this.userQuery = '';
3604
+ this.isStylesheetInjected = false;
3605
+ }
3606
+ connectedCallback() {
3607
+ super.connectedCallback();
3608
+ if (!this.isStylesheetInjected && this.css) {
3609
+ const sheet = new CSSStyleSheet();
3610
+ sheet.replaceSync(this.css);
3611
+ this.shadowRoot.adoptedStyleSheets.push(sheet);
3612
+ this.isStylesheetInjected = true;
3613
+ }
3488
3614
  }
3489
3615
  scrollToBottom() {
3490
3616
  var _a;
@@ -3555,12 +3681,12 @@ class ChatSection extends r$2 {
3555
3681
  return x `
3556
3682
  <div class="message-wrapper">
3557
3683
  <div class="message bot">
3558
- <div>
3559
- <div class="bot-icon">${botIcon}</div>
3560
- </div>
3684
+ <div>${this.renderBotIcon()}</div>
3561
3685
  <div>
3562
3686
  ${message.message
3563
- ? x ` <markdown-renderer>${message.message}</markdown-renderer>`
3687
+ ? x ` <markdown-renderer
3688
+ .content=${message.message}
3689
+ ></markdown-renderer>`
3564
3690
  : E}
3565
3691
  ${this.viewType !== 'modal' && ((_a = message.products) === null || _a === void 0 ? void 0 : _a[0])
3566
3692
  ? x `
@@ -3605,6 +3731,14 @@ class ChatSection extends r$2 {
3605
3731
  </div>
3606
3732
  `;
3607
3733
  }
3734
+ renderBotIcon() {
3735
+ if (this.botIconUrl) {
3736
+ return x `<div class="bot-icon">
3737
+ <img src=${this.botIconUrl} width="30" height="30" />
3738
+ </div>`;
3739
+ }
3740
+ return x ` <div class="bot-icon">${botIcon}</div> `;
3741
+ }
3608
3742
  chatWindow() {
3609
3743
  if (this.isLoadingHistory || this.isLoadingThreads) {
3610
3744
  return x `<div class="messages loading">
@@ -3615,17 +3749,13 @@ class ChatSection extends r$2 {
3615
3749
  <div class="messages">
3616
3750
  ${this.isTyping
3617
3751
  ? x ` <div class="message bot">
3618
- <div>
3619
- <div class="bot-icon">${botIcon}</div>
3620
- </div>
3752
+ <div>${this.renderBotIcon()}</div>
3621
3753
  ${this.typingIndicator()}
3622
3754
  </div>`
3623
3755
  : ''}
3624
3756
  ${this.isFailed
3625
3757
  ? x `<div class="message bot">
3626
- <div>
3627
- <div class="bot-icon">${botIcon}</div>
3628
- </div>
3758
+ <div>${this.renderBotIcon()}</div>
3629
3759
  <div>
3630
3760
  <p>
3631
3761
  Uh-oh! Looks like I tripped over some alpha-stage wires.
@@ -3636,16 +3766,14 @@ class ChatSection extends r$2 {
3636
3766
  </div>
3637
3767
  </div>`
3638
3768
  : E}
3639
- ${this.messages.map((message) => {
3769
+ ${o$1(this.messages, (message) => {
3640
3770
  if (message.sender === 'bot') {
3641
3771
  return this.botMessage(message);
3642
3772
  }
3643
3773
  return x ` <div class="message user">${message.message}</div> `;
3644
3774
  })}
3645
3775
  <div class="message bot">
3646
- <div>
3647
- <div class="bot-icon">${botIcon}</div>
3648
- </div>
3776
+ <div>${this.renderBotIcon()}</div>
3649
3777
  <div>
3650
3778
  <p>
3651
3779
  Hi,
@@ -3658,28 +3786,39 @@ class ChatSection extends r$2 {
3658
3786
  </div>
3659
3787
  `;
3660
3788
  }
3661
- quickPrompts() {
3789
+ renderPrompts() {
3662
3790
  if (this.isLoadingHistory || this.isTyping || this.isLoadingThreads) {
3663
3791
  return E;
3664
3792
  }
3793
+ const isWelcomeMessage = this.messages.length === 1 && this.messages[0].sender === 'bot';
3665
3794
  const prompts = this.messages.length
3666
3795
  ? this.messages[0].welcomePrompts
3667
3796
  : this.prompts
3668
3797
  ? this.prompts.split(',').map((prompt) => prompt.trim())
3669
3798
  : ['Best Sellers'];
3670
- if (!prompts) {
3799
+ const customPrompts = !this.messages.length || isWelcomeMessage ? this.customPrompts : undefined;
3800
+ if (!prompts && !customPrompts) {
3671
3801
  return E;
3672
3802
  }
3673
3803
  return x `
3674
3804
  <div class="prompts btn">
3675
- ${prompts.map((prompt) => x `
3676
- <div
3677
- class="prompt"
3678
- @click=${(e) => this.processMessage(e, prompt)}
3679
- >
3680
- ${prompt}
3681
- </div>
3682
- `)}
3805
+ ${o$1(prompts, (prompt) => {
3806
+ return x `
3807
+ <div
3808
+ class="prompt"
3809
+ @click=${(e) => this.processMessage(e, prompt)}
3810
+ >
3811
+ ${prompt}
3812
+ </div>
3813
+ `;
3814
+ })}
3815
+ ${o$1(customPrompts, ({ prompt, link }) => {
3816
+ return x `
3817
+ <a class="prompt" href=${link} target="_blank" rel="noopener">
3818
+ ${prompt}
3819
+ </a>
3820
+ `;
3821
+ })}
3683
3822
  </div>
3684
3823
  `;
3685
3824
  }
@@ -3759,7 +3898,11 @@ class ChatSection extends r$2 {
3759
3898
  modalViewHeader() {
3760
3899
  var _a;
3761
3900
  return x `
3762
- <div>${botIcon}</div>
3901
+ <div>
3902
+ ${this.botIconUrl
3903
+ ? x `<img src=${this.botIconUrl} width="30" height="30" />`
3904
+ : botIcon}
3905
+ </div>
3763
3906
  <h2>${((_a = this.thread) === null || _a === void 0 ? void 0 : _a.title) || 'New Search'}</h2>
3764
3907
  <div class="btns-wrapper">
3765
3908
  <tooltip-component .position=${'bottom-right'} .text=${'New Chat'}>
@@ -3926,7 +4069,7 @@ class ChatSection extends r$2 {
3926
4069
  'modal-view': this.viewType === 'modal',
3927
4070
  })}
3928
4071
  >
3929
- ${this.chatWindow()} ${this.quickPrompts()}
4072
+ ${this.chatWindow()} ${this.renderPrompts()}
3930
4073
  <form class="chat-form" @submit=${this.onSubmit}>
3931
4074
  <input
3932
4075
  type="text"
@@ -3945,6 +4088,11 @@ class ChatSection extends r$2 {
3945
4088
  ${sendFilledIcon}
3946
4089
  </button>
3947
4090
  </form>
4091
+ ${this.viewType === 'modal'
4092
+ ? x ` <footer>
4093
+ Powered by <a href="https://blotout.io">Blotout</a>
4094
+ </footer>`
4095
+ : E}
3948
4096
  </div>
3949
4097
  <personalize-dialog
3950
4098
  .createChatThread=${this.createChatThread.bind(this)}
@@ -4001,6 +4149,10 @@ __decorate([
4001
4149
  n({ type: String }),
4002
4150
  __metadata("design:type", Object)
4003
4151
  ], ChatSection.prototype, "merchantImage", void 0);
4152
+ __decorate([
4153
+ n({ type: String }),
4154
+ __metadata("design:type", Object)
4155
+ ], ChatSection.prototype, "botIconUrl", void 0);
4004
4156
  __decorate([
4005
4157
  n({ type: String }),
4006
4158
  __metadata("design:type", Object)
@@ -4009,6 +4161,10 @@ __decorate([
4009
4161
  n({ type: String }),
4010
4162
  __metadata("design:type", Object)
4011
4163
  ], ChatSection.prototype, "prompts", void 0);
4164
+ __decorate([
4165
+ n({ type: Array }),
4166
+ __metadata("design:type", Object)
4167
+ ], ChatSection.prototype, "customPrompts", void 0);
4012
4168
  __decorate([
4013
4169
  n({ type: Boolean }),
4014
4170
  __metadata("design:type", Boolean)
@@ -4085,15 +4241,69 @@ __decorate([
4085
4241
  n({ type: String }),
4086
4242
  __metadata("design:type", Object)
4087
4243
  ], ChatSection.prototype, "userQuery", void 0);
4244
+ __decorate([
4245
+ n({ type: String }),
4246
+ __metadata("design:type", String)
4247
+ ], ChatSection.prototype, "css", void 0);
4088
4248
  if (!customElements.get('chat-section')) {
4089
4249
  customElements.define('chat-section', ChatSection);
4090
4250
  }
4091
4251
 
4252
+ const handleEventSource = async (response, onEvent) => {
4253
+ var _a, _b;
4254
+ if (!response.body) {
4255
+ throw new Error('No body found in response');
4256
+ }
4257
+ let buffer = '';
4258
+ for await (const chunk of response.body.pipeThrough(new TextDecoderStream())) {
4259
+ buffer += chunk;
4260
+ const sections = buffer.split('\n\n');
4261
+ buffer = sections.pop() || '';
4262
+ for (const section of sections) {
4263
+ const event = parseMessage(section, true);
4264
+ onEvent((_a = event.event) !== null && _a !== void 0 ? _a : '', event.data);
4265
+ }
4266
+ }
4267
+ if (buffer) {
4268
+ const event = parseMessage(buffer, true);
4269
+ onEvent((_b = event.event) !== null && _b !== void 0 ? _b : '', event.data);
4270
+ }
4271
+ };
4272
+ const parseMessage = (message, parseDataAsJSON) => {
4273
+ const fields = message.split('\n');
4274
+ const result = {};
4275
+ for (const field of fields) {
4276
+ if (field.startsWith(':')) {
4277
+ continue;
4278
+ }
4279
+ if (field.startsWith('event: ')) {
4280
+ result.event = field.slice(7);
4281
+ }
4282
+ else if (field.startsWith('data: ')) {
4283
+ if (result.data) {
4284
+ result.data += '\n';
4285
+ }
4286
+ else {
4287
+ result.data = '';
4288
+ }
4289
+ result.data += field.slice(6);
4290
+ }
4291
+ else if (field.startsWith('retry: ')) {
4292
+ result.retry = field.slice(7);
4293
+ }
4294
+ }
4295
+ if (result.data && parseDataAsJSON) {
4296
+ result.data = JSON.parse(result.data);
4297
+ }
4298
+ return result;
4299
+ };
4300
+
4092
4301
  const DIALOG_DELAY = 1000;
4093
4302
  const normalizePath = (path) => path.replace(/\/$/, '');
4094
4303
  class ShopGPT extends r$2 {
4095
4304
  constructor() {
4096
4305
  super(...arguments);
4306
+ this.isStylesheetInjected = false;
4097
4307
  this.latestThreadLoad = DEFAULT_MAX_THREAD_AGE;
4098
4308
  this.modalState = 'close';
4099
4309
  this.isLoadingHistory = false;
@@ -4104,6 +4314,7 @@ class ShopGPT extends r$2 {
4104
4314
  this.products = [];
4105
4315
  this.messages = [];
4106
4316
  this.chatThreads = new Map();
4317
+ this.customPrompts = [];
4107
4318
  this.loadData = async () => {
4108
4319
  if (!this.shopGPTAPI) {
4109
4320
  return;
@@ -4126,7 +4337,13 @@ class ShopGPT extends r$2 {
4126
4337
  }
4127
4338
  connectedCallback() {
4128
4339
  super.connectedCallback();
4129
- if (!this.uiMode || this.uiMode === 'overlay') {
4340
+ if (!this.isStylesheetInjected && this.css) {
4341
+ const sheet = new CSSStyleSheet();
4342
+ sheet.replaceSync(this.css);
4343
+ this.shadowRoot.adoptedStyleSheets.push(sheet);
4344
+ this.isStylesheetInjected = true;
4345
+ }
4346
+ if (!this.view || this.view === 'overlay') {
4130
4347
  if (!this.path) {
4131
4348
  return;
4132
4349
  }
@@ -4147,7 +4364,7 @@ class ShopGPT extends r$2 {
4147
4364
  init() {
4148
4365
  window.addEventListener('edgetag-initialized', this.loadData);
4149
4366
  window.addEventListener('popstate', this.onPopState);
4150
- if (!this.uiMode || this.uiMode === 'overlay') {
4367
+ if (!this.view || this.view === 'overlay') {
4151
4368
  delay(DIALOG_DELAY).then(() => {
4152
4369
  var _a;
4153
4370
  if (document.hidden) {
@@ -4195,27 +4412,12 @@ class ShopGPT extends r$2 {
4195
4412
  : undefined;
4196
4413
  try {
4197
4414
  this.isTyping = true;
4198
- const reply = await this.shopGPTAPI.processQuery('', thread.threadId, productHandle);
4199
- if (reply.chatTitle) {
4200
- this.setChatTitle(this.selectedThreadId, reply.chatTitle);
4201
- }
4202
- this.messages = [
4203
- {
4204
- messageId: reply.messageId,
4205
- sender: 'bot',
4206
- message: reply.message,
4207
- products: reply.products,
4208
- welcomePrompts: reply.welcomePrompts,
4209
- },
4210
- ...this.messages,
4211
- ];
4212
- this.products = reply.products || [];
4415
+ const response = await this.shopGPTAPI.processQuery('', thread.threadId, productHandle);
4416
+ this.processMessageResponse(response);
4213
4417
  }
4214
4418
  catch (error) {
4215
4419
  logger.error(error);
4216
4420
  this.isFailed = true;
4217
- }
4218
- finally {
4219
4421
  this.isTyping = false;
4220
4422
  }
4221
4423
  }
@@ -4286,10 +4488,25 @@ class ShopGPT extends r$2 {
4286
4488
  this.isLoadingHistory = false;
4287
4489
  }
4288
4490
  }
4491
+ async loadCustomPrompts(threadId) {
4492
+ try {
4493
+ if (!threadId) {
4494
+ this.customPrompts = [];
4495
+ return;
4496
+ }
4497
+ this.customPrompts = await this.shopGPTAPI.fetchCustomPrompts(threadId);
4498
+ }
4499
+ catch (e) {
4500
+ logger.error(e);
4501
+ }
4502
+ }
4289
4503
  async setSelectedThreadId(threadId) {
4290
4504
  this.isFailed = false;
4291
4505
  this.selectedThreadId = threadId;
4292
- await this.loadHistory(threadId);
4506
+ await Promise.all([
4507
+ this.loadHistory(threadId),
4508
+ this.loadCustomPrompts(threadId),
4509
+ ]);
4293
4510
  }
4294
4511
  async createChatThread(payload, loadInitialQuery) {
4295
4512
  try {
@@ -4300,7 +4517,9 @@ class ShopGPT extends r$2 {
4300
4517
  ]);
4301
4518
  this.selectedThreadId = thread.threadId;
4302
4519
  if (loadInitialQuery) {
4303
- await this.loadInitialQuery();
4520
+ await Promise.all([
4521
+ (this.loadInitialQuery(), this.loadCustomPrompts(thread.threadId)),
4522
+ ]);
4304
4523
  }
4305
4524
  }
4306
4525
  catch (e) {
@@ -4336,6 +4555,92 @@ class ShopGPT extends r$2 {
4336
4555
  .catch(logger.error)
4337
4556
  .finally(() => (this.isLoadingThreads = false));
4338
4557
  }
4558
+ handleMessageChunks(messageData) {
4559
+ if (!messageData.message) {
4560
+ return;
4561
+ }
4562
+ const latestMessage = this.messages[0];
4563
+ this.isTyping = false;
4564
+ if (latestMessage === null || latestMessage === void 0 ? void 0 : latestMessage.isChunk) {
4565
+ this.messages = [
4566
+ {
4567
+ ...latestMessage,
4568
+ message: latestMessage.message + messageData.message,
4569
+ isChunk: messageData.isChunk,
4570
+ sender: 'bot',
4571
+ },
4572
+ ...this.messages.slice(1),
4573
+ ];
4574
+ }
4575
+ else {
4576
+ this.messages = [
4577
+ {
4578
+ sender: 'bot',
4579
+ message: messageData.message || '',
4580
+ isChunk: messageData.isChunk,
4581
+ },
4582
+ ...this.messages,
4583
+ ];
4584
+ }
4585
+ }
4586
+ handleCompleteMessage(messageData) {
4587
+ const latestMessage = this.messages[0];
4588
+ this.messages = [
4589
+ {
4590
+ ...latestMessage,
4591
+ message: messageData.message || '',
4592
+ messageId: messageData.messageId,
4593
+ welcomePrompts: messageData.welcomePrompts,
4594
+ sender: 'bot',
4595
+ isChunk: false,
4596
+ },
4597
+ ...this.messages.slice(1),
4598
+ ];
4599
+ }
4600
+ handleProductsComplete(products, isMessageCompleted) {
4601
+ if (!products || !isMessageCompleted) {
4602
+ return;
4603
+ }
4604
+ const latestMessage = this.messages[0];
4605
+ this.messages = [
4606
+ {
4607
+ ...latestMessage,
4608
+ products,
4609
+ },
4610
+ ...this.messages.slice(1),
4611
+ ];
4612
+ if (products.length) {
4613
+ this.products = products;
4614
+ }
4615
+ }
4616
+ processMessageResponse(response) {
4617
+ let isMessageCompleted = false;
4618
+ let products;
4619
+ handleEventSource(response, (eventName, data) => {
4620
+ if (eventName === 'ChatTitle') {
4621
+ this.setChatTitle(this.selectedThreadId, data.chatTitle || '');
4622
+ }
4623
+ else if (eventName === 'Message') {
4624
+ this.handleMessageChunks(data);
4625
+ }
4626
+ else if (eventName === 'MessageComplete') {
4627
+ isMessageCompleted = true;
4628
+ this.handleProductsComplete(products, isMessageCompleted);
4629
+ this.handleCompleteMessage(data);
4630
+ }
4631
+ else if (eventName === 'ProductsComplete') {
4632
+ this.handleProductsComplete(data.products, isMessageCompleted);
4633
+ products = data.products;
4634
+ }
4635
+ else if (eventName === 'error') {
4636
+ if (this.messages[0].isChunk) {
4637
+ this.messages = this.messages.slice(1);
4638
+ }
4639
+ this.isFailed = true;
4640
+ throw new Error(data);
4641
+ }
4642
+ });
4643
+ }
4339
4644
  async sendMessageToServer(e, message) {
4340
4645
  e.preventDefault();
4341
4646
  e.stopPropagation();
@@ -4346,32 +4651,12 @@ class ShopGPT extends r$2 {
4346
4651
  try {
4347
4652
  this.messages = [{ sender: 'user', message }, ...this.messages];
4348
4653
  this.isTyping = true;
4349
- const reply = await this.submitQuery(message);
4350
- if (!reply) {
4351
- return;
4352
- }
4353
- if (reply.chatTitle) {
4354
- this.setChatTitle(this.selectedThreadId, reply.chatTitle);
4355
- }
4356
- this.messages = [
4357
- {
4358
- messageId: reply.messageId,
4359
- sender: 'bot',
4360
- message: reply.message,
4361
- products: reply.products,
4362
- welcomePrompts: reply.welcomePrompts,
4363
- },
4364
- ...this.messages,
4365
- ];
4366
- if (reply.products && reply.products.length > 0) {
4367
- this.products = reply.products;
4368
- }
4654
+ const response = await this.submitQuery(message);
4655
+ this.processMessageResponse(response);
4369
4656
  }
4370
4657
  catch (err) {
4371
4658
  logger.error(err);
4372
4659
  this.isFailed = true;
4373
- }
4374
- finally {
4375
4660
  this.isTyping = false;
4376
4661
  }
4377
4662
  }
@@ -4393,7 +4678,7 @@ class ShopGPT extends r$2 {
4393
4678
  return this.storeAPI.getSiteCurrency();
4394
4679
  }
4395
4680
  render() {
4396
- if (this.uiMode === 'modal') {
4681
+ if (this.view === 'modal') {
4397
4682
  return this.modalMode();
4398
4683
  }
4399
4684
  return this.overlayMode();
@@ -4418,6 +4703,7 @@ class ShopGPT extends r$2 {
4418
4703
  .isLoading=${this.isLoadingThreads}
4419
4704
  .isTyping=${this.isTyping}
4420
4705
  .merchantUrl=${this.merchantUrl}
4706
+ .css=${this.css}
4421
4707
  ></chat-threads>
4422
4708
  <products-section
4423
4709
  .merchantImage=${this.merchantImage}
@@ -4425,6 +4711,7 @@ class ShopGPT extends r$2 {
4425
4711
  .isLoadingHistory=${this.isLoadingHistory}
4426
4712
  .siteCurrency=${this.getSiteCurrency()}
4427
4713
  .isLoadingThreads=${this.isLoadingThreads}
4714
+ .css=${this.css}
4428
4715
  ></products-section>
4429
4716
  <chat-section
4430
4717
  .prompts=${this.quickPrompts}
@@ -4442,6 +4729,9 @@ class ShopGPT extends r$2 {
4442
4729
  .profiles=${this.profiles}
4443
4730
  .viewType=${'overlay'}
4444
4731
  .isLoadingThreads=${this.isLoadingThreads}
4732
+ .customPrompts=${this.customPrompts}
4733
+ .botIconUrl=${this.botIconUrl}
4734
+ .css=${this.css}
4445
4735
  ></chat-section>
4446
4736
  </div>
4447
4737
  </dialog>
@@ -4492,6 +4782,9 @@ class ShopGPT extends r$2 {
4492
4782
  .chatThreads=${this.chatThreads}
4493
4783
  .isLoadingThreads=${this.isLoadingThreads}
4494
4784
  .merchantImage=${this.merchantImage}
4785
+ .customPrompts=${this.customPrompts}
4786
+ .botIconUrl=${this.botIconUrl}
4787
+ .css=${this.css}
4495
4788
  ></chat-section>
4496
4789
  </div>
4497
4790
  `;
@@ -4542,6 +4835,10 @@ __decorate([
4542
4835
  n({ type: Object }),
4543
4836
  __metadata("design:type", Map)
4544
4837
  ], ShopGPT.prototype, "chatThreads", void 0);
4838
+ __decorate([
4839
+ n({ type: Array }),
4840
+ __metadata("design:type", Array)
4841
+ ], ShopGPT.prototype, "customPrompts", void 0);
4545
4842
  if (!customElements.get('shop-gpt')) {
4546
4843
  customElements.define('shop-gpt', ShopGPT);
4547
4844
  }
@@ -4563,12 +4860,14 @@ if (typeof window != 'undefined' && typeof document != 'undefined') {
4563
4860
  shopGPT.merchantUrl = params.merchantUrl;
4564
4861
  shopGPT.profiles = params.profiles;
4565
4862
  shopGPT.productHandles = params.productHandles;
4566
- shopGPT.uiMode = params.uiMode;
4863
+ shopGPT.view = params.view;
4567
4864
  shopGPT.path = params.path;
4568
4865
  shopGPT.brandName = params.brandName;
4569
4866
  shopGPT.quickPrompts = params.quickPrompts;
4570
4867
  shopGPT.merchantImage = params.merchantImage;
4571
4868
  shopGPT.latestThreadLoad = params.latestThreadLoad;
4869
+ shopGPT.botIconUrl = params.botIconUrl;
4870
+ shopGPT.css = params.css;
4572
4871
  document.body.append(shopGPT);
4573
4872
  },
4574
4873
  destroy() {