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