@memori.ai/memori-react 9.0.0-alpha.7 → 9.0.0-alpha.9

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 (102) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/components/Chat/Chat.css +48 -7
  3. package/dist/components/Chat/Chat.d.ts +1 -0
  4. package/dist/components/Chat/Chat.js +7 -2
  5. package/dist/components/Chat/Chat.js.map +1 -1
  6. package/dist/components/ChatBubble/ChatBubble.css +16 -14
  7. package/dist/components/ChatInputs/ChatInputs.css +233 -100
  8. package/dist/components/ChatTextArea/ChatTextArea.css +28 -6
  9. package/dist/components/KnownFacts/KnownFacts.js +2 -2
  10. package/dist/components/KnownFacts/KnownFacts.js.map +1 -1
  11. package/dist/components/MemoriWidget/MemoriWidget.css +1 -3
  12. package/dist/components/MemoriWidget/MemoriWidget.js +18 -15
  13. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  14. package/dist/components/MicrophoneButton/MicrophoneButton.css +50 -18
  15. package/dist/components/PoweredBy/PoweredBy.css +10 -2
  16. package/dist/components/UploadButton/UploadImages/UploadImages.js +1 -1
  17. package/dist/components/UploadButton/UploadImages/UploadImages.js.map +1 -1
  18. package/dist/components/layouts/Chat.js +5 -5
  19. package/dist/components/layouts/Chat.js.map +1 -1
  20. package/dist/components/layouts/HiddenChat.js +10 -236
  21. package/dist/components/layouts/HiddenChat.js.map +1 -1
  22. package/dist/components/layouts/WebsiteAssistant/WebsiteAssistant.js +23 -2
  23. package/dist/components/layouts/WebsiteAssistant/WebsiteAssistant.js.map +1 -1
  24. package/dist/components/layouts/WebsiteAssistant/website-assistant.css +138 -55
  25. package/dist/components/layouts/ZoomedFullBody.js +2 -2
  26. package/dist/components/layouts/ZoomedFullBody.js.map +1 -1
  27. package/dist/components/layouts/chat.css +42 -29
  28. package/dist/components/layouts/fullpage.css +29 -22
  29. package/dist/components/layouts/hidden-chat.css +147 -33
  30. package/dist/components/layouts/zoomed-full-body.css +33 -32
  31. package/dist/helpers/positionPopover.d.ts +1 -0
  32. package/dist/helpers/positionPopover.js +7 -0
  33. package/dist/helpers/positionPopover.js.map +1 -0
  34. package/dist/styles.css +1 -1
  35. package/dist/version.d.ts +1 -1
  36. package/dist/version.js +1 -1
  37. package/esm/components/Chat/Chat.css +48 -7
  38. package/esm/components/Chat/Chat.d.ts +1 -0
  39. package/esm/components/Chat/Chat.js +7 -2
  40. package/esm/components/Chat/Chat.js.map +1 -1
  41. package/esm/components/ChatBubble/ChatBubble.css +16 -14
  42. package/esm/components/ChatInputs/ChatInputs.css +233 -100
  43. package/esm/components/ChatTextArea/ChatTextArea.css +28 -6
  44. package/esm/components/KnownFacts/KnownFacts.js +2 -2
  45. package/esm/components/KnownFacts/KnownFacts.js.map +1 -1
  46. package/esm/components/MemoriWidget/MemoriWidget.css +1 -3
  47. package/esm/components/MemoriWidget/MemoriWidget.js +18 -15
  48. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  49. package/esm/components/MicrophoneButton/MicrophoneButton.css +50 -18
  50. package/esm/components/PoweredBy/PoweredBy.css +10 -2
  51. package/esm/components/UploadButton/UploadImages/UploadImages.js +1 -1
  52. package/esm/components/UploadButton/UploadImages/UploadImages.js.map +1 -1
  53. package/esm/components/layouts/Chat.js +5 -5
  54. package/esm/components/layouts/Chat.js.map +1 -1
  55. package/esm/components/layouts/HiddenChat.js +12 -238
  56. package/esm/components/layouts/HiddenChat.js.map +1 -1
  57. package/esm/components/layouts/WebsiteAssistant/WebsiteAssistant.js +23 -2
  58. package/esm/components/layouts/WebsiteAssistant/WebsiteAssistant.js.map +1 -1
  59. package/esm/components/layouts/WebsiteAssistant/website-assistant.css +138 -55
  60. package/esm/components/layouts/ZoomedFullBody.js +2 -2
  61. package/esm/components/layouts/ZoomedFullBody.js.map +1 -1
  62. package/esm/components/layouts/chat.css +42 -29
  63. package/esm/components/layouts/fullpage.css +29 -22
  64. package/esm/components/layouts/hidden-chat.css +147 -33
  65. package/esm/components/layouts/zoomed-full-body.css +33 -32
  66. package/esm/helpers/positionPopover.d.ts +1 -0
  67. package/esm/helpers/positionPopover.js +4 -0
  68. package/esm/helpers/positionPopover.js.map +1 -0
  69. package/esm/styles.css +1 -1
  70. package/esm/version.d.ts +1 -1
  71. package/esm/version.js +1 -1
  72. package/package.json +1 -1
  73. package/src/components/Chat/Chat.css +48 -7
  74. package/src/components/Chat/Chat.tsx +11 -0
  75. package/src/components/ChatBubble/ChatBubble.css +16 -14
  76. package/src/components/ChatInputs/ChatInputs.css +233 -100
  77. package/src/components/ChatTextArea/ChatTextArea.css +28 -6
  78. package/src/components/KnownFacts/KnownFacts.tsx +2 -0
  79. package/src/components/MemoriWidget/MemoriWidget.css +1 -3
  80. package/src/components/MemoriWidget/MemoriWidget.tsx +397 -384
  81. package/src/components/MicrophoneButton/MicrophoneButton.css +50 -18
  82. package/src/components/PoweredBy/PoweredBy.css +10 -2
  83. package/src/components/UploadButton/UploadImages/UploadImages.tsx +1 -1
  84. package/src/components/layouts/Chat.tsx +42 -44
  85. package/src/components/layouts/HiddenChat.tsx +16 -413
  86. package/src/components/layouts/WebsiteAssistant/WebsiteAssistant.tsx +59 -15
  87. package/src/components/layouts/WebsiteAssistant/website-assistant.css +138 -55
  88. package/src/components/layouts/ZoomedFullBody.tsx +3 -1
  89. package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +227 -236
  90. package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +30 -40
  91. package/src/components/layouts/__snapshots__/HiddenChat.test.tsx.snap +165 -66
  92. package/src/components/layouts/__snapshots__/Totem.test.tsx.snap +15 -20
  93. package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +153 -158
  94. package/src/components/layouts/chat.css +42 -29
  95. package/src/components/layouts/fullpage.css +29 -22
  96. package/src/components/layouts/hidden-chat.css +147 -33
  97. package/src/components/layouts/layouts.stories.tsx +1 -0
  98. package/src/components/layouts/zoomed-full-body.css +33 -32
  99. package/src/helpers/positionPopover.test.ts +33 -0
  100. package/src/helpers/positionPopover.ts +13 -0
  101. package/src/styles.css +34 -13
  102. package/src/version.ts +1 -1
@@ -24,6 +24,7 @@ import type {
24
24
  PiiDetectionConfig,
25
25
  } from '../../types/layout';
26
26
  import { checkPii } from '../../helpers/piiDetection'; // PII check when integrationConfig.layout has piiDetection.enabled
27
+ import { shouldRestartSessionOnPositionPopoverClose } from '../../helpers/positionPopover';
27
28
 
28
29
  // Libraries
29
30
  import React, {
@@ -514,20 +515,23 @@ export function getChatStyles(
514
515
 
515
516
  const primaryContent = config?.buttonTextColor ?? '#ffffff';
516
517
 
518
+ // Match @memori.ai/ui token semantics:
519
+ // --memori-surface-contrast = ink (#000 light / #fff dark)
520
+ // --memori-surface-contrast-inverse = surface (#fff light / #000 dark)
517
521
  return {
518
522
  '--memori-primary-color': primary,
519
523
  '--memori-primary': primary,
520
524
  '--memori-primary-content': primaryContent,
521
525
  '--memori-primary-hover':
522
- 'color-mix(in oklch, var(--memori-primary), var(--memori-surface-contrast-inverse, black) 15%)',
526
+ 'color-mix(in oklch, var(--memori-primary), var(--memori-surface-contrast, black) 15%)',
523
527
  '--memori-primary-active':
524
- 'color-mix(in oklch, var(--memori-primary), var(--memori-surface-contrast-inverse, black) 25%)',
528
+ 'color-mix(in oklch, var(--memori-primary), var(--memori-surface-contrast, black) 25%)',
525
529
  '--memori-primary-disabled':
526
530
  'color-mix(in oklch, var(--memori-primary), transparent 60%)',
527
531
  '--memori-primary-subtle':
528
- 'color-mix(in oklch, var(--memori-primary), var(--memori-surface-contrast, white) 60%)',
532
+ 'color-mix(in oklch, var(--memori-primary), var(--memori-surface-contrast-inverse, white) 60%)',
529
533
  '--memori-primary-subtle-hover':
530
- 'color-mix(in oklch, var(--memori-primary), var(--memori-surface-contrast, white) 50%)',
534
+ 'color-mix(in oklch, var(--memori-primary), var(--memori-surface-contrast-inverse, white) 50%)',
531
535
  '--memori-border-primary':
532
536
  'color-mix(in oklch, var(--memori-primary), transparent 70%)',
533
537
  '--memori-border-primary-hover':
@@ -538,9 +542,9 @@ export function getChatStyles(
538
542
  '--memori-shadow-primary':
539
543
  '0 8px 16px -4px color-mix(in oklch, var(--memori-primary), transparent 70%)',
540
544
  '--memori-skeleton-base':
541
- 'color-mix(in oklch, var(--memori-primary), var(--memori-surface-contrast, white) 85%)',
545
+ 'color-mix(in oklch, var(--memori-primary), var(--memori-surface-contrast-inverse, white) 85%)',
542
546
  '--memori-skeleton-highlight':
543
- 'color-mix(in oklch, var(--memori-primary), var(--memori-surface-contrast, white) 75%)',
547
+ 'color-mix(in oklch, var(--memori-primary), var(--memori-surface-contrast-inverse, white) 75%)',
544
548
  } as CSSProperties;
545
549
  }
546
550
 
@@ -942,7 +946,10 @@ const MemoriWidget = ({
942
946
 
943
947
  if (!additionalInfo?.loginToken && !authToken) {
944
948
  setLoginToken(getLocalConfig<typeof loginToken>('loginToken', undefined));
945
- userTokenRef.current = getLocalConfig<typeof loginToken>('loginToken', undefined);
949
+ userTokenRef.current = getLocalConfig<typeof loginToken>(
950
+ 'loginToken',
951
+ undefined
952
+ );
946
953
 
947
954
  setBirthDate(getLocalConfig<string | undefined>('birthDate', undefined));
948
955
  }
@@ -1782,12 +1789,11 @@ const MemoriWidget = ({
1782
1789
  if (currentState.emission && !suppressHistoryUpdate) {
1783
1790
  // Determine initial status message based on context
1784
1791
  // Show status message only if session expired and there's existing history
1785
- const initialStatus =
1786
- sessionExpiredStatus
1787
- ? sessionExpiredStatus
1788
- : history.length <= 1
1789
- ? true
1790
- : undefined;
1792
+ const initialStatus = sessionExpiredStatus
1793
+ ? sessionExpiredStatus
1794
+ : history.length <= 1
1795
+ ? true
1796
+ : undefined;
1791
1797
 
1792
1798
  // Set initial message or append to existing history
1793
1799
  history.length <= 1
@@ -1937,7 +1943,9 @@ const MemoriWidget = ({
1937
1943
  }, 500);
1938
1944
  };
1939
1945
 
1940
- const handleReopenFailure = (state: Awaited<ReturnType<typeof reopenSession>>) => {
1946
+ const handleReopenFailure = (
1947
+ state: Awaited<ReturnType<typeof reopenSession>>
1948
+ ) => {
1941
1949
  setMemoriTyping(false);
1942
1950
  setTypingText(undefined);
1943
1951
  // `null` = explicit failure; `undefined` = auth/age modal opened (message kept in history)
@@ -2639,9 +2647,6 @@ const MemoriWidget = ({
2639
2647
  >();
2640
2648
 
2641
2649
  const globalBackground = integrationConfig?.globalBackground;
2642
- const globalBackgroundUrl = globalBackground
2643
- ? `url(${globalBackground})`
2644
- : null;
2645
2650
 
2646
2651
  const integrationProperties = (
2647
2652
  integration ? getChatStyles(integrationConfig) : {}
@@ -2684,7 +2689,7 @@ const MemoriWidget = ({
2684
2689
  }
2685
2690
  if (integrationConfig?.blurBackground) {
2686
2691
  integrationStylesheetParts.push(
2687
- '.memori-widget .memori--global-background { filter: blur(5px); }'
2692
+ '.memori-widget .memori-chat--content--has-global-background::before { filter: blur(5px); }'
2688
2693
  );
2689
2694
  }
2690
2695
  const integrationStylesheet = `@layer integration {
@@ -2930,79 +2935,79 @@ const MemoriWidget = ({
2930
2935
  });
2931
2936
 
2932
2937
  if (session?.dialogState) {
2933
- // reset history
2934
- if (!chatLog) {
2935
- setHistory([]);
2938
+ // reset history
2939
+ if (!chatLog) {
2940
+ setHistory([]);
2936
2941
 
2937
- // Use translateAndSpeak which already handles the speaking
2938
- await translateAndSpeak(session.dialogState, userLang);
2939
- // No need for additional handleSpeak call since translateAndSpeak already handles it
2940
- setHasUserActivatedSpeak(true);
2941
- setClickedStart(false);
2942
- } else {
2943
- const messages = chatLog.lines.map(
2944
- (l, i) =>
2945
- ({
2946
- text: l.text,
2947
- media: l.media
2948
- ?.filter(m => allowedMediaTypes.includes(m.mimeType))
2949
- ?.map(m => ({
2950
- mediumID: `${i}-${m.mimeType}`,
2951
- ...m,
2952
- })),
2953
- fromUser: l.inbound,
2954
- llmUsage: (l as any).llmUsage,
2955
- timestamp: l.timestamp,
2956
- emitter: l.emitter,
2957
- initial: i === 0,
2958
- } as Message)
2959
- );
2942
+ // Use translateAndSpeak which already handles the speaking
2943
+ await translateAndSpeak(session.dialogState, userLang);
2944
+ // No need for additional handleSpeak call since translateAndSpeak already handles it
2945
+ setHasUserActivatedSpeak(true);
2946
+ setClickedStart(false);
2947
+ } else {
2948
+ const messages = chatLog.lines.map(
2949
+ (l, i) =>
2950
+ ({
2951
+ text: l.text,
2952
+ media: l.media
2953
+ ?.filter(m => allowedMediaTypes.includes(m.mimeType))
2954
+ ?.map(m => ({
2955
+ mediumID: `${i}-${m.mimeType}`,
2956
+ ...m,
2957
+ })),
2958
+ fromUser: l.inbound,
2959
+ llmUsage: (l as any).llmUsage,
2960
+ timestamp: l.timestamp,
2961
+ emitter: l.emitter,
2962
+ initial: i === 0,
2963
+ } as Message)
2964
+ );
2960
2965
 
2961
- // we dont remove the last one as it is the current state
2962
- translatedMessages = messages ?? [];
2963
- if (
2964
- language.toUpperCase() !== userLang.toUpperCase() &&
2965
- isMultilanguageEnabled
2966
- ) {
2967
- try {
2968
- translatedMessages = await Promise.all(
2969
- messages.map(async m => {
2970
- // If original text is present, the message is already translated
2971
- if ('originalText' in m && m.originalText) {
2972
- return m;
2973
- }
2974
- // Otherwise translate the message
2975
- return {
2976
- ...m,
2977
- originalText: m.text,
2978
- text: (
2979
- await getTranslation(
2980
- m.text,
2981
- userLang,
2982
- language,
2983
- baseUrl
2984
- )
2985
- ).text,
2986
- };
2987
- })
2988
- );
2989
- } catch {
2990
- // ignore translation errors
2966
+ // we dont remove the last one as it is the current state
2967
+ translatedMessages = messages ?? [];
2968
+ if (
2969
+ language.toUpperCase() !== userLang.toUpperCase() &&
2970
+ isMultilanguageEnabled
2971
+ ) {
2972
+ try {
2973
+ translatedMessages = await Promise.all(
2974
+ messages.map(async m => {
2975
+ // If original text is present, the message is already translated
2976
+ if ('originalText' in m && m.originalText) {
2977
+ return m;
2978
+ }
2979
+ // Otherwise translate the message
2980
+ return {
2981
+ ...m,
2982
+ originalText: m.text,
2983
+ text: (
2984
+ await getTranslation(
2985
+ m.text,
2986
+ userLang,
2987
+ language,
2988
+ baseUrl
2989
+ )
2990
+ ).text,
2991
+ };
2992
+ })
2993
+ );
2994
+ } catch {
2995
+ // ignore translation errors
2996
+ }
2991
2997
  }
2992
- }
2993
2998
 
2994
- setHistory(translatedMessages);
2999
+ setHistory(translatedMessages);
2995
3000
 
2996
- translateDialogState(
2997
- session.dialogState,
2998
- userLang,
2999
- undefined,
3000
- true
3001
- ).finally(() => {
3002
- setHasUserActivatedSpeak(true);
3003
- setClickedStart(false);
3004
- });
3005
- }
3001
+ translateDialogState(
3002
+ session.dialogState,
3003
+ userLang,
3004
+ undefined,
3005
+ true
3006
+ ).finally(() => {
3007
+ setHasUserActivatedSpeak(true);
3008
+ setClickedStart(false);
3009
+ });
3010
+ }
3006
3011
  } else if (session?.resultCode === 0) {
3007
3012
  sessionStartingRef.current = false;
3008
3013
  await onClickStart((session as any) || undefined);
@@ -3244,7 +3249,10 @@ const MemoriWidget = ({
3244
3249
  setTypingText(undefined);
3245
3250
  }
3246
3251
  } else if (response.resultCode === 0) {
3247
- logWidgetError('onClickStart enter-text missing correlationID', response);
3252
+ logWidgetError(
3253
+ 'onClickStart enter-text missing correlationID',
3254
+ response
3255
+ );
3248
3256
  }
3249
3257
  }
3250
3258
  }
@@ -3264,10 +3272,17 @@ const MemoriWidget = ({
3264
3272
 
3265
3273
  const setPositionPopoverOpen = useCallback(
3266
3274
  (open: boolean) => {
3267
- setPositionPopoverOpenState(open);
3268
- if (!open && autoStart) {
3269
- onClickStart();
3270
- }
3275
+ setPositionPopoverOpenState(wasOpen => {
3276
+ if (
3277
+ shouldRestartSessionOnPositionPopoverClose(wasOpen, open, autoStart)
3278
+ ) {
3279
+ // Defer: React setState updaters must stay pure.
3280
+ queueMicrotask(() => {
3281
+ onClickStart();
3282
+ });
3283
+ }
3284
+ return open;
3285
+ });
3271
3286
  },
3272
3287
  [autoStart, onClickStart]
3273
3288
  );
@@ -3637,19 +3652,14 @@ const MemoriWidget = ({
3637
3652
  isChatlogPanel:
3638
3653
  (integrationConfig as { artifactChatLogPanel?: boolean } | undefined)
3639
3654
  ?.artifactChatLogPanel === true,
3655
+ globalBackground: globalBackground || undefined,
3640
3656
  };
3641
3657
 
3642
- const integrationBackground =
3643
- integration && globalBackgroundUrl ? (
3644
- <div className="memori--global-background">
3645
- <div
3646
- className="memori--global-background-image"
3647
- style={{ backgroundImage: globalBackgroundUrl }}
3648
- />
3649
- </div>
3650
- ) : (
3651
- <div className="memori--global-background no-background-image" />
3652
- );
3658
+ // Layout chrome placeholder (e.g. Totem gradient when no image). The actual
3659
+ // integration background image is applied on `.memori-chat--content` via chatProps.
3660
+ const integrationBackground = (
3661
+ <div className="memori--global-background no-background-image" />
3662
+ );
3653
3663
 
3654
3664
  const integrationStyle =
3655
3665
  integration &&
@@ -3749,293 +3759,296 @@ const MemoriWidget = ({
3749
3759
  }
3750
3760
  />
3751
3761
 
3752
- <ArtifactAPIBridge
3753
- pushMessage={(message: Message) => {
3754
- setHistory(history => {
3755
- if (!history.length) return history;
3756
- const lastMessage = history[history.length - 1];
3757
- if (!lastMessage || lastMessage.fromUser) return history;
3758
- // Create a new message object with the updated text
3759
- const updatedLastMessage = {
3760
- ...lastMessage,
3761
- text: lastMessage.text + message.text,
3762
- };
3763
- return [...history.slice(0, -1), updatedLastMessage];
3764
- });
3765
- }}
3766
- />
3767
-
3768
- <audio
3769
- id="memori-audio"
3770
- style={{ display: 'none' }}
3771
- src="https://aisuru.com/intro.mp3"
3772
- />
3773
-
3774
- {isClient && (
3775
- <MemoriAuth
3776
- withModal
3777
- pwdOrTokens={authModalState}
3778
- openModal={!!authModalState}
3779
- setPwdOrTokens={setAuthModalState}
3780
- showTokens={memori.privacyType === 'SECRET'}
3781
- onFinish={(values: any) => {
3782
- if (values['password']) setMemoriPwd(values['password']);
3783
- if (values['password']) memoriPassword = values['password'];
3784
- if (values['tokens']) setMemoriTokens(values['tokens']);
3785
-
3786
- return reopenSession(
3787
- !sessionId,
3788
- values['password'],
3789
- values['tokens'],
3790
- personification?.tag,
3791
- personification?.pin,
3792
- {
3793
- LANG: userLang,
3794
- PATHNAME: window.location.pathname?.toUpperCase(),
3795
- ROUTE:
3796
- window.location.pathname
3797
- ?.split('/')
3798
- ?.pop()
3799
- ?.toUpperCase() || '',
3800
- ...(initialContextVars || {}),
3801
- },
3802
- initialQuestion,
3803
- birthDate
3804
- )
3805
- .then(state => {
3806
- if (!state?.sessionID) {
3807
- throw new Error('AUTH_FAILED');
3808
- }
3762
+ <ArtifactAPIBridge
3763
+ pushMessage={(message: Message) => {
3764
+ setHistory(history => {
3765
+ if (!history.length) return history;
3766
+ const lastMessage = history[history.length - 1];
3767
+ if (!lastMessage || lastMessage.fromUser) return history;
3768
+ // Create a new message object with the updated text
3769
+ const updatedLastMessage = {
3770
+ ...lastMessage,
3771
+ text: lastMessage.text + message.text,
3772
+ };
3773
+ return [...history.slice(0, -1), updatedLastMessage];
3774
+ });
3775
+ }}
3776
+ />
3809
3777
 
3810
- setAuthModalState(null);
3811
- // If we got a valid state from reopenSession, don't call onClickStart again
3812
- // to avoid duplicate snippet execution
3813
- if (state?.dialogState) {
3814
- setHasUserActivatedSpeak(true);
3778
+ <audio
3779
+ id="memori-audio"
3780
+ style={{ display: 'none' }}
3781
+ src="https://aisuru.com/intro.mp3"
3782
+ />
3783
+
3784
+ {isClient && (
3785
+ <MemoriAuth
3786
+ withModal
3787
+ pwdOrTokens={authModalState}
3788
+ openModal={!!authModalState}
3789
+ setPwdOrTokens={setAuthModalState}
3790
+ showTokens={memori.privacyType === 'SECRET'}
3791
+ onFinish={(values: any) => {
3792
+ if (values['password']) setMemoriPwd(values['password']);
3793
+ if (values['password']) memoriPassword = values['password'];
3794
+ if (values['tokens']) setMemoriTokens(values['tokens']);
3795
+
3796
+ return reopenSession(
3797
+ !sessionId,
3798
+ values['password'],
3799
+ values['tokens'],
3800
+ personification?.tag,
3801
+ personification?.pin,
3802
+ {
3803
+ LANG: userLang,
3804
+ PATHNAME: window.location.pathname?.toUpperCase(),
3805
+ ROUTE:
3806
+ window.location.pathname
3807
+ ?.split('/')
3808
+ ?.pop()
3809
+ ?.toUpperCase() || '',
3810
+ ...(initialContextVars || {}),
3811
+ },
3812
+ initialQuestion,
3813
+ birthDate
3814
+ )
3815
+ .then(state => {
3816
+ if (!state?.sessionID) {
3817
+ throw new Error('AUTH_FAILED');
3818
+ }
3819
+
3820
+ setAuthModalState(null);
3821
+ // If we got a valid state from reopenSession, don't call onClickStart again
3822
+ // to avoid duplicate snippet execution
3823
+ if (state?.dialogState) {
3824
+ setHasUserActivatedSpeak(true);
3825
+ } else {
3826
+ // Only call onClickStart if reopenSession didn't return a valid state
3827
+ onClickStart(state);
3828
+ }
3829
+ })
3830
+ .catch(error => {
3831
+ if (
3832
+ !(error instanceof Error) ||
3833
+ error.message !== 'AUTH_FAILED'
3834
+ ) {
3835
+ setGotErrorInOpening(true);
3836
+ }
3837
+ throw error;
3838
+ });
3839
+ }}
3840
+ minimumNumberOfRecoveryTokens={
3841
+ memori?.minimumNumberOfRecoveryTokens ?? 1
3842
+ }
3843
+ />
3844
+ )}
3845
+
3846
+ {isClient && (
3847
+ <AgeVerificationModal
3848
+ visible={showAgeVerification}
3849
+ minAge={minAge}
3850
+ onClose={birthDate => {
3851
+ if (birthDate) {
3852
+ setBirthDate(birthDate);
3853
+
3854
+ setLocalConfig('birthDate', birthDate);
3855
+
3856
+ reopenSession(
3857
+ !sessionId,
3858
+ memoriPassword || memoriPwd || memori?.secretToken,
3859
+ memoriTokens,
3860
+ personification?.tag,
3861
+ personification?.pin,
3862
+ {
3863
+ LANG: userLang,
3864
+ PATHNAME: window.location.pathname?.toUpperCase(),
3865
+ ROUTE:
3866
+ window.location.pathname
3867
+ ?.split('/')
3868
+ ?.pop()
3869
+ ?.toUpperCase() || '',
3870
+ ...(initialContextVars || {}),
3871
+ },
3872
+ initialQuestion,
3873
+ birthDate
3874
+ )
3875
+ .then(state => {
3876
+ setShowAgeVerification(false);
3877
+ setAuthModalState(null);
3878
+ onClickStart(state || undefined);
3879
+ })
3880
+ .catch(() => {
3881
+ setShowAgeVerification(false);
3882
+ setGotErrorInOpening(true);
3883
+ });
3815
3884
  } else {
3816
- // Only call onClickStart if reopenSession didn't return a valid state
3817
- onClickStart(state);
3818
- }
3819
- })
3820
- .catch(error => {
3821
- if (
3822
- !(error instanceof Error) ||
3823
- error.message !== 'AUTH_FAILED'
3824
- ) {
3825
- setGotErrorInOpening(true);
3826
- }
3827
- throw error;
3828
- });
3829
- }}
3830
- minimumNumberOfRecoveryTokens={
3831
- memori?.minimumNumberOfRecoveryTokens ?? 1
3832
- }
3833
- />
3834
- )}
3835
-
3836
- {isClient && (
3837
- <AgeVerificationModal
3838
- visible={showAgeVerification}
3839
- minAge={minAge}
3840
- onClose={birthDate => {
3841
- if (birthDate) {
3842
- setBirthDate(birthDate);
3843
-
3844
- setLocalConfig('birthDate', birthDate);
3845
-
3846
- reopenSession(
3847
- !sessionId,
3848
- memoriPassword || memoriPwd || memori?.secretToken,
3849
- memoriTokens,
3850
- personification?.tag,
3851
- personification?.pin,
3852
- {
3853
- LANG: userLang,
3854
- PATHNAME: window.location.pathname?.toUpperCase(),
3855
- ROUTE:
3856
- window.location.pathname
3857
- ?.split('/')
3858
- ?.pop()
3859
- ?.toUpperCase() || '',
3860
- ...(initialContextVars || {}),
3861
- },
3862
- initialQuestion,
3863
- birthDate
3864
- )
3865
- .then(state => {
3866
3885
  setShowAgeVerification(false);
3867
- setAuthModalState(null);
3868
- onClickStart(state || undefined);
3869
- })
3870
- .catch(() => {
3871
- setShowAgeVerification(false);
3872
- setGotErrorInOpening(true);
3886
+ setClickedStart(false);
3887
+ }
3888
+ }}
3889
+ />
3890
+ )}
3891
+
3892
+ {showSettingsDrawer && (
3893
+ <SettingsDrawer
3894
+ layout={selectedLayout}
3895
+ open={!!showSettingsDrawer}
3896
+ onClose={() => setShowSettingsDrawer(false)}
3897
+ microphoneMode={
3898
+ continuousSpeech ? 'CONTINUOUS' : 'HOLD_TO_TALK'
3899
+ }
3900
+ continuousSpeechTimeout={continuousSpeechTimeout}
3901
+ setMicrophoneMode={mode =>
3902
+ setContinuousSpeech(mode === 'CONTINUOUS')
3903
+ }
3904
+ setContinuousSpeechTimeout={setContinuousSpeechTimeout}
3905
+ controlsPosition={controlsPosition}
3906
+ setControlsPosition={setControlsPosition}
3907
+ hideEmissions={hideEmissions}
3908
+ setHideEmissions={setHideEmissions}
3909
+ avatarType={avatarType}
3910
+ setAvatarType={setAvatarType}
3911
+ enablePositionControls={enablePositionControls}
3912
+ setEnablePositionControls={setEnablePositionControls}
3913
+ isAvatar3d={!!integrationConfig?.avatarURL}
3914
+ additionalSettings={additionalSettings}
3915
+ speakerMuted={speakerMuted}
3916
+ />
3917
+ )}
3918
+
3919
+ {showChatHistoryDrawer && (
3920
+ <ChatHistoryDrawer
3921
+ open={!!showChatHistoryDrawer}
3922
+ onClose={() => setShowChatHistoryDrawer(false)}
3923
+ resumeSession={chatLog => {
3924
+ setChatLogID(chatLog.chatLogID);
3925
+ onClickStart(undefined, false, chatLog);
3926
+ setShowChatHistoryDrawer(false);
3927
+ }}
3928
+ apiClient={client}
3929
+ sessionId={sessionId || ''}
3930
+ memori={memori}
3931
+ baseUrl={baseUrl}
3932
+ history={history}
3933
+ apiUrl={client.constants.BACKEND_URL}
3934
+ loginToken={loginToken}
3935
+ language={language}
3936
+ userLang={userLang}
3937
+ isMultilanguageEnabled={isMultilanguageEnabled}
3938
+ />
3939
+ )}
3940
+
3941
+ {showKnownFactsDrawer && sessionId && (
3942
+ <KnownFacts
3943
+ apiClient={client}
3944
+ memori={memori}
3945
+ sessionID={sessionId}
3946
+ visible={showKnownFactsDrawer}
3947
+ closeDrawer={() => setShowKnownFactsDrawer(false)}
3948
+ />
3949
+ )}
3950
+
3951
+ {showExpertsDrawer && !!experts && (
3952
+ <ExpertsDrawer
3953
+ apiUrl={client.constants.BACKEND_URL}
3954
+ baseUrl={baseUrl}
3955
+ tenant={tenant}
3956
+ experts={experts}
3957
+ open={showExpertsDrawer}
3958
+ onClose={() => setShowExpertsDrawer(false)}
3959
+ />
3960
+ )}
3961
+
3962
+ {showLoginDrawer && tenant?.name && (
3963
+ <LoginDrawer
3964
+ tenant={tenant}
3965
+ apiClient={client}
3966
+ open={!!showLoginDrawer}
3967
+ user={user}
3968
+ loginToken={loginToken}
3969
+ onClose={() => setShowLoginDrawer(false)}
3970
+ drawerClassName={
3971
+ selectedLayout === 'WEBSITE_ASSISTANT'
3972
+ ? 'memori-drawer--above-website-assistant'
3973
+ : undefined
3974
+ }
3975
+ onLogin={(user, token) => {
3976
+ //The user is logged in, so we need to set open a new session with the new token
3977
+ reopenSession(
3978
+ false,
3979
+ memoriPassword || memoriPwd || memori?.secretToken,
3980
+ [],
3981
+ personification?.tag,
3982
+ personification?.pin,
3983
+ {
3984
+ LANG: userLang,
3985
+ PATHNAME: window.location.pathname?.toUpperCase(),
3986
+ ROUTE:
3987
+ window.location.pathname
3988
+ ?.split('/')
3989
+ ?.pop()
3990
+ ?.toUpperCase() || '',
3991
+ ...(initialContextVars || {}),
3992
+ },
3993
+ undefined, // Don't send initialQuestion after login, only show the login status chip
3994
+ birthDate,
3995
+ { loginToken: token } as any,
3996
+ undefined,
3997
+ sessionId
3998
+ ).then(state => {
3999
+ setShowLoginDrawer(false);
4000
+ setUser(user);
4001
+ setLoginToken(token);
4002
+ userTokenRef.current = token;
4003
+ setLocalConfig('loginToken', token);
4004
+ // Push a message with initial status to show status message when a new session is created after login
4005
+ if (
4006
+ state?.sessionID &&
4007
+ state.sessionID !== sessionId &&
4008
+ state?.dialogState
4009
+ ) {
4010
+ // Push a message with initial status message showing successful login
4011
+ // Only show the chip component, not the emission text
4012
+ const username = user?.userName || t('login.user');
4013
+ pushMessage({
4014
+ text: '', // Empty text so only the chip is visible
4015
+ emitter: state.dialogState.emitter,
4016
+ media:
4017
+ state.dialogState.emittedMedia ??
4018
+ state.dialogState.media ??
4019
+ [],
4020
+ fromUser: false,
4021
+ initial: t('login.successfullyLoggedIn', {
4022
+ username,
4023
+ }) as any,
4024
+ contextVars: state.dialogState.contextVars,
4025
+ date: state.dialogState.currentDate,
4026
+ placeName: state.dialogState.currentPlaceName,
4027
+ placeLatitude: state.dialogState.currentLatitude,
4028
+ placeLongitude: state.dialogState.currentLongitude,
4029
+ placeUncertaintyKm:
4030
+ state.dialogState.currentUncertaintyKm,
4031
+ tag: state.dialogState.currentTag,
4032
+ memoryTags: state.dialogState.memoryTags,
4033
+ });
4034
+ // Update the dialog state so the UI reflects the new session
4035
+ setCurrentDialogState(state.dialogState);
4036
+ }
3873
4037
  });
3874
- } else {
3875
- setShowAgeVerification(false);
3876
- setClickedStart(false);
3877
- }
3878
- }}
3879
- />
3880
- )}
3881
-
3882
- {showSettingsDrawer && (
3883
- <SettingsDrawer
3884
- layout={selectedLayout}
3885
- open={!!showSettingsDrawer}
3886
- onClose={() => setShowSettingsDrawer(false)}
3887
- microphoneMode={continuousSpeech ? 'CONTINUOUS' : 'HOLD_TO_TALK'}
3888
- continuousSpeechTimeout={continuousSpeechTimeout}
3889
- setMicrophoneMode={mode =>
3890
- setContinuousSpeech(mode === 'CONTINUOUS')
3891
- }
3892
- setContinuousSpeechTimeout={setContinuousSpeechTimeout}
3893
- controlsPosition={controlsPosition}
3894
- setControlsPosition={setControlsPosition}
3895
- hideEmissions={hideEmissions}
3896
- setHideEmissions={setHideEmissions}
3897
- avatarType={avatarType}
3898
- setAvatarType={setAvatarType}
3899
- enablePositionControls={enablePositionControls}
3900
- setEnablePositionControls={setEnablePositionControls}
3901
- isAvatar3d={!!integrationConfig?.avatarURL}
3902
- additionalSettings={additionalSettings}
3903
- speakerMuted={speakerMuted}
3904
- />
3905
- )}
3906
-
3907
- {showChatHistoryDrawer && (
3908
- <ChatHistoryDrawer
3909
- open={!!showChatHistoryDrawer}
3910
- onClose={() => setShowChatHistoryDrawer(false)}
3911
- resumeSession={chatLog => {
3912
- setChatLogID(chatLog.chatLogID);
3913
- onClickStart(undefined, false, chatLog);
3914
- setShowChatHistoryDrawer(false);
3915
- }}
3916
- apiClient={client}
3917
- sessionId={sessionId || ''}
3918
- memori={memori}
3919
- baseUrl={baseUrl}
3920
- history={history}
3921
- apiUrl={client.constants.BACKEND_URL}
3922
- loginToken={loginToken}
3923
- language={language}
3924
- userLang={userLang}
3925
- isMultilanguageEnabled={isMultilanguageEnabled}
3926
- />
3927
- )}
3928
-
3929
- {showKnownFactsDrawer && sessionId && (
3930
- <KnownFacts
3931
- apiClient={client}
3932
- memori={memori}
3933
- sessionID={sessionId}
3934
- visible={showKnownFactsDrawer}
3935
- closeDrawer={() => setShowKnownFactsDrawer(false)}
3936
- />
3937
- )}
3938
-
3939
- {showExpertsDrawer && !!experts && (
3940
- <ExpertsDrawer
3941
- apiUrl={client.constants.BACKEND_URL}
3942
- baseUrl={baseUrl}
3943
- tenant={tenant}
3944
- experts={experts}
3945
- open={showExpertsDrawer}
3946
- onClose={() => setShowExpertsDrawer(false)}
3947
- />
3948
- )}
3949
-
3950
- {showLoginDrawer && tenant?.name && (
3951
- <LoginDrawer
3952
- tenant={tenant}
3953
- apiClient={client}
3954
- open={!!showLoginDrawer}
3955
- user={user}
3956
- loginToken={loginToken}
3957
- onClose={() => setShowLoginDrawer(false)}
3958
- drawerClassName={
3959
- selectedLayout === 'WEBSITE_ASSISTANT'
3960
- ? 'memori-drawer--above-website-assistant'
3961
- : undefined
3962
- }
3963
- onLogin={(user, token) => {
3964
- //The user is logged in, so we need to set open a new session with the new token
3965
- reopenSession(
3966
- false,
3967
- memoriPassword || memoriPwd || memori?.secretToken,
3968
- [],
3969
- personification?.tag,
3970
- personification?.pin,
3971
- {
3972
- LANG: userLang,
3973
- PATHNAME: window.location.pathname?.toUpperCase(),
3974
- ROUTE:
3975
- window.location.pathname
3976
- ?.split('/')
3977
- ?.pop()
3978
- ?.toUpperCase() || '',
3979
- ...(initialContextVars || {}),
3980
- },
3981
- undefined, // Don't send initialQuestion after login, only show the login status chip
3982
- birthDate,
3983
- { loginToken: token } as any,
3984
- undefined,
3985
- sessionId
3986
- ).then(state => {
3987
- setShowLoginDrawer(false);
3988
- setUser(user);
3989
- setLoginToken(token);
3990
- userTokenRef.current = token;
3991
- setLocalConfig('loginToken', token);
3992
- // Push a message with initial status to show status message when a new session is created after login
3993
- if (
3994
- state?.sessionID &&
3995
- state.sessionID !== sessionId &&
3996
- state?.dialogState
3997
- ) {
3998
- // Push a message with initial status message showing successful login
3999
- // Only show the chip component, not the emission text
4000
- const username = user?.userName || t('login.user');
4001
- pushMessage({
4002
- text: '', // Empty text so only the chip is visible
4003
- emitter: state.dialogState.emitter,
4004
- media:
4005
- state.dialogState.emittedMedia ??
4006
- state.dialogState.media ??
4007
- [],
4008
- fromUser: false,
4009
- initial: t('login.successfullyLoggedIn', {
4010
- username,
4011
- }) as any,
4012
- contextVars: state.dialogState.contextVars,
4013
- date: state.dialogState.currentDate,
4014
- placeName: state.dialogState.currentPlaceName,
4015
- placeLatitude: state.dialogState.currentLatitude,
4016
- placeLongitude: state.dialogState.currentLongitude,
4017
- placeUncertaintyKm: state.dialogState.currentUncertaintyKm,
4018
- tag: state.dialogState.currentTag,
4019
- memoryTags: state.dialogState.memoryTags,
4038
+ }}
4039
+ setUser={setUser}
4040
+ onLogout={() => {
4041
+ if (!loginToken) return;
4042
+ client.backend.pwlUserLogout(loginToken).then(() => {
4043
+ setShowLoginDrawer(false);
4044
+ setUser(undefined);
4045
+ setLoginToken(undefined);
4046
+ userTokenRef.current = undefined;
4047
+ removeLocalConfig('loginToken');
4020
4048
  });
4021
- // Update the dialog state so the UI reflects the new session
4022
- setCurrentDialogState(state.dialogState);
4023
- }
4024
- });
4025
- }}
4026
- setUser={setUser}
4027
- onLogout={() => {
4028
- if (!loginToken) return;
4029
- client.backend.pwlUserLogout(loginToken).then(() => {
4030
- setShowLoginDrawer(false);
4031
- setUser(undefined);
4032
- setLoginToken(undefined);
4033
- userTokenRef.current = undefined;
4034
- removeLocalConfig('loginToken');
4035
- });
4036
- }}
4037
- />
4038
- )}
4049
+ }}
4050
+ />
4051
+ )}
4039
4052
  </div>
4040
4053
  </WidgetSurfaceProvider>
4041
4054
  </MemoriUIProvider>