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