@memori.ai/memori-react 8.21.0 → 8.23.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 (63) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/components/DrawerFooter/DrawerFooter.css +63 -0
  3. package/dist/components/DrawerFooter/DrawerFooter.d.ts +12 -0
  4. package/dist/components/DrawerFooter/DrawerFooter.js +10 -0
  5. package/dist/components/DrawerFooter/DrawerFooter.js.map +1 -0
  6. package/dist/components/MemoriWidget/MemoriWidget.d.ts +4 -1
  7. package/dist/components/MemoriWidget/MemoriWidget.js +71 -87
  8. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  9. package/dist/components/layouts/WebsiteAssistant.js +3 -3
  10. package/dist/components/layouts/WebsiteAssistant.js.map +1 -1
  11. package/dist/icons/FacebookIcon.d.ts +3 -0
  12. package/dist/icons/FacebookIcon.js +6 -0
  13. package/dist/icons/FacebookIcon.js.map +1 -0
  14. package/dist/icons/LinkedinIcon.d.ts +3 -0
  15. package/dist/icons/LinkedinIcon.js +6 -0
  16. package/dist/icons/LinkedinIcon.js.map +1 -0
  17. package/dist/icons/TelegramIcon.d.ts +3 -0
  18. package/dist/icons/TelegramIcon.js +6 -0
  19. package/dist/icons/TelegramIcon.js.map +1 -0
  20. package/dist/icons/TwitterIcon.d.ts +3 -0
  21. package/dist/icons/TwitterIcon.js +6 -0
  22. package/dist/icons/TwitterIcon.js.map +1 -0
  23. package/dist/icons/WhatsappIcon.d.ts +3 -0
  24. package/dist/icons/WhatsappIcon.js +6 -0
  25. package/dist/icons/WhatsappIcon.js.map +1 -0
  26. package/dist/index.js +6 -2
  27. package/dist/index.js.map +1 -1
  28. package/dist/version.d.ts +1 -1
  29. package/dist/version.js +1 -1
  30. package/esm/components/DrawerFooter/DrawerFooter.css +63 -0
  31. package/esm/components/DrawerFooter/DrawerFooter.d.ts +12 -0
  32. package/esm/components/DrawerFooter/DrawerFooter.js +8 -0
  33. package/esm/components/DrawerFooter/DrawerFooter.js.map +1 -0
  34. package/esm/components/MemoriWidget/MemoriWidget.d.ts +4 -1
  35. package/esm/components/MemoriWidget/MemoriWidget.js +71 -87
  36. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  37. package/esm/components/layouts/WebsiteAssistant.js +3 -3
  38. package/esm/components/layouts/WebsiteAssistant.js.map +1 -1
  39. package/esm/icons/FacebookIcon.d.ts +3 -0
  40. package/esm/icons/FacebookIcon.js +4 -0
  41. package/esm/icons/FacebookIcon.js.map +1 -0
  42. package/esm/icons/LinkedinIcon.d.ts +3 -0
  43. package/esm/icons/LinkedinIcon.js +4 -0
  44. package/esm/icons/LinkedinIcon.js.map +1 -0
  45. package/esm/icons/TelegramIcon.d.ts +3 -0
  46. package/esm/icons/TelegramIcon.js +4 -0
  47. package/esm/icons/TelegramIcon.js.map +1 -0
  48. package/esm/icons/TwitterIcon.d.ts +3 -0
  49. package/esm/icons/TwitterIcon.js +4 -0
  50. package/esm/icons/TwitterIcon.js.map +1 -0
  51. package/esm/icons/WhatsappIcon.d.ts +3 -0
  52. package/esm/icons/WhatsappIcon.js +4 -0
  53. package/esm/icons/WhatsappIcon.js.map +1 -0
  54. package/esm/index.js +6 -2
  55. package/esm/index.js.map +1 -1
  56. package/esm/version.d.ts +1 -1
  57. package/esm/version.js +1 -1
  58. package/package.json +1 -1
  59. package/src/components/MemoriWidget/MemoriWidget.tsx +100 -102
  60. package/src/components/layouts/WebsiteAssistant.tsx +25 -22
  61. package/src/components/layouts/layouts.stories.tsx +21 -0
  62. package/src/index.tsx +5 -1
  63. package/src/version.ts +1 -1
package/esm/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '8.21.0';
1
+ export const version = '8.23.0';
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "8.21.0",
2
+ "version": "8.23.0",
3
3
  "name": "@memori.ai/memori-react",
4
4
  "author": "Memori Srl",
5
5
  "main": "dist/index.js",
@@ -32,7 +32,7 @@ import React, {
32
32
  } from 'react';
33
33
  import { useTranslation } from 'react-i18next';
34
34
  import memoriApiClient from '@memori.ai/memori-api-client';
35
- import { AudioContext, IAudioContext } from 'standardized-audio-context';
35
+ import { IAudioContext } from 'standardized-audio-context';
36
36
  import cx from 'classnames';
37
37
  import { DateTime } from 'luxon';
38
38
  import toast from 'react-hot-toast';
@@ -72,7 +72,6 @@ import {
72
72
  hasTouchscreen,
73
73
  stripDuplicates,
74
74
  installMathJax,
75
- stripReasoningTags,
76
75
  } from '../../helpers/utils';
77
76
  import { getTTSVoice } from '../../helpers/tts/ttsVoiceUtility';
78
77
  import {
@@ -82,7 +81,6 @@ import {
82
81
  } from '../../helpers/constants';
83
82
  import { getErrori18nKey } from '../../helpers/error';
84
83
  import { getCredits } from '../../helpers/credits';
85
- import { useViseme } from '../../context/visemeContext';
86
84
  import { sanitizeText } from '../../helpers/sanitizer';
87
85
  import { TTSConfig, useTTS } from '../../helpers/tts/useTTS';
88
86
  import ChatHistoryDrawer from '../ChatHistoryDrawer/ChatHistory';
@@ -369,6 +367,8 @@ export interface LayoutProps {
369
367
  loading?: boolean;
370
368
  autoStart?: boolean;
371
369
  onSidebarToggle?: (isOpen: boolean) => void;
370
+ /** When true (e.g. from integrationConfig for website_assistant layout), hide the 3D avatar. */
371
+ avatar3dHidden?: boolean;
372
372
  }
373
373
 
374
374
  export interface Props {
@@ -378,6 +378,10 @@ export interface Props {
378
378
  tenantID: string;
379
379
  memoriConfigs?: MemoriConfig[];
380
380
  memoriLang?: string;
381
+ /** UI language: labels, buttons, page translation (i18n) */
382
+ uiLang?: string;
383
+ /** Spoken/chat language: select box in StartPanel and conversation language */
384
+ spokenLang?: string;
381
385
  multilingual?: boolean;
382
386
  integration?: Integration;
383
387
  layout?: LayoutName;
@@ -441,6 +445,8 @@ const MemoriWidget = ({
441
445
  ownerUserName,
442
446
  tenantID,
443
447
  memoriLang,
448
+ uiLang,
449
+ spokenLang,
444
450
  multilingual,
445
451
  integration,
446
452
  layout,
@@ -505,7 +511,6 @@ const MemoriWidget = ({
505
511
  postTextEnteredEvent,
506
512
  postPlaceChangedEvent,
507
513
  postDateChangedEvent,
508
- postTimeoutEvent,
509
514
  postTagChangedEvent,
510
515
  getSession,
511
516
  getExpertReferences,
@@ -565,7 +570,8 @@ const MemoriWidget = ({
565
570
  : !!integrationConfig?.multilanguage;
566
571
  const forcedTimeout = integrationConfig?.forcedTimeout as number | undefined;
567
572
  const [userLang, setUserLang] = useState(
568
- memoriLang ??
573
+ spokenLang ??
574
+ memoriLang ??
569
575
  integrationConfig?.lang ??
570
576
  language ??
571
577
  integrationConfig?.uiLang ??
@@ -573,6 +579,13 @@ const MemoriWidget = ({
573
579
  'IT'
574
580
  );
575
581
 
582
+ // Sync userLang when parent passes spokenLang (select box in StartPanel)
583
+ useEffect(() => {
584
+ if (spokenLang != null) {
585
+ setUserLang(spokenLang);
586
+ }
587
+ }, [spokenLang]);
588
+
576
589
  const applyMathFormatting =
577
590
  useMathFormatting !== undefined
578
591
  ? useMathFormatting
@@ -582,18 +595,20 @@ const MemoriWidget = ({
582
595
  }, [applyMathFormatting]);
583
596
 
584
597
  /**
585
- * Sets the language in the i18n instance
598
+ * Sets the UI language in the i18n instance (page translation). uiLang takes precedence.
586
599
  */
587
600
  useEffect(() => {
588
- if (
589
- isMultilanguageEnabled &&
590
- userLang &&
591
- uiLanguages.includes(userLang.toLowerCase())
592
- ) {
601
+ const langToApply =
602
+ uiLang && uiLanguages.includes(uiLang.toLowerCase())
603
+ ? uiLang.toLowerCase()
604
+ : userLang && uiLanguages.includes(userLang.toLowerCase())
605
+ ? userLang.toLowerCase()
606
+ : null;
607
+ if (langToApply && typeof i18n?.changeLanguage === 'function') {
593
608
  // @ts-ignore
594
- i18n.changeLanguage(userLang.toLowerCase());
609
+ i18n.changeLanguage(langToApply);
595
610
  }
596
- }, [userLang]);
611
+ }, [uiLang, userLang]);
597
612
 
598
613
  const [loading, setLoading] = useState(false);
599
614
  const [memoriTyping, setMemoriTyping] = useState<boolean>(false);
@@ -604,8 +619,8 @@ const MemoriWidget = ({
604
619
  typeof layout === 'string'
605
620
  ? layout
606
621
  : typeof integrationConfig?.layout === 'string'
607
- ? integrationConfig.layout
608
- : integrationConfig?.layout?.name;
622
+ ? integrationConfig.layout
623
+ : integrationConfig?.layout?.name;
609
624
  const selectedLayout = layoutName || 'DEFAULT';
610
625
  const piiDetection: PiiDetectionConfig | undefined =
611
626
  typeof integrationConfig?.layout === 'object' &&
@@ -614,6 +629,21 @@ const MemoriWidget = ({
614
629
  ? integrationConfig.layout.piiDetection
615
630
  : undefined;
616
631
 
632
+ // website_assistant: hide 3D avatar from layout when integrationConfig has avatar_3d_hidden
633
+ const layoutObj =
634
+ typeof layout === 'object' && layout !== null ? layout : null;
635
+ const integrationLayoutObj =
636
+ typeof integrationConfig?.layout === 'object' &&
637
+ integrationConfig?.layout !== null
638
+ ? (integrationConfig.layout as { avatar_3d_hidden?: boolean })
639
+ : null;
640
+ const avatar3dHidden =
641
+ selectedLayout === 'WEBSITE_ASSISTANT' &&
642
+ ((layoutObj as { avatar_3d_hidden?: boolean } | null)?.avatar_3d_hidden ===
643
+ true ||
644
+ integrationLayoutObj?.avatar_3d_hidden === true ||
645
+ integrationConfig?.avatar_3d_hidden === true);
646
+
617
647
  const defaultEnableAudio =
618
648
  enableAudio ?? integrationConfig?.enableAudio ?? true;
619
649
 
@@ -816,6 +846,10 @@ const MemoriWidget = ({
816
846
  (window.getMemoriState() as MemoriSession)?.sessionID;
817
847
  if (!sessionID || !text?.length) return;
818
848
 
849
+ if (memori.needsDateTime) {
850
+ await sendDateChangedEvent({ sessionID: sessionID });
851
+ }
852
+
819
853
  // Build full message text (same as what will be sent) so we can run PII check on it.
820
854
  // Order: user text -> optional translation -> appended document attachment content.
821
855
  let msg = text;
@@ -837,9 +871,7 @@ const MemoriWidget = ({
837
871
  m => (m as any).type === 'document' && m.properties?.isAttachedFile
838
872
  );
839
873
  if (mediaDocuments && mediaDocuments.length > 0) {
840
- const documentContents = mediaDocuments
841
- .map(doc => doc.content)
842
- .join(' ');
874
+ const documentContents = mediaDocuments.map(doc => doc.content).join(' ');
843
875
  msg = msg + ' ' + documentContents;
844
876
  }
845
877
 
@@ -990,14 +1022,17 @@ const MemoriWidget = ({
990
1022
  }
991
1023
  });
992
1024
  } else if (response.resultCode === 500 && response.resultMessage) {
993
- setHistory(h => [...h, {
994
- text: 'Error: ' + response.resultMessage,
995
- emitter: 'system',
996
- fromUser: false,
997
- initial: false,
998
- contextVars: {},
999
- date: new Date().toISOString(),
1000
- }]);
1025
+ setHistory(h => [
1026
+ ...h,
1027
+ {
1028
+ text: 'Error: ' + response.resultMessage,
1029
+ emitter: 'system',
1030
+ fromUser: false,
1031
+ initial: false,
1032
+ contextVars: {},
1033
+ date: new Date().toISOString(),
1034
+ },
1035
+ ]);
1001
1036
  } else {
1002
1037
  console.warn('[SEND_MESSAGE]', response);
1003
1038
  return Promise.reject(response);
@@ -1357,7 +1392,11 @@ const MemoriWidget = ({
1357
1392
  params.additionalInfo?.loginToken ??
1358
1393
  additionalInfo?.loginToken ??
1359
1394
  authToken,
1360
- language: (userLang ?? memori.culture?.split('-')?.[0] ?? 'IT').toLowerCase(),
1395
+ language: (
1396
+ userLang ??
1397
+ memori.culture?.split('-')?.[0] ??
1398
+ 'IT'
1399
+ ).toLowerCase(),
1361
1400
  referral: referral,
1362
1401
  timeZoneOffset: new Date().getTimezoneOffset().toString(),
1363
1402
  },
@@ -1381,6 +1420,13 @@ const MemoriWidget = ({
1381
1420
  if (position && memori.needsPosition)
1382
1421
  applyPosition(position, session.sessionID);
1383
1422
 
1423
+ if (memori.needsDateTime) {
1424
+ await sendDateChangedEvent({
1425
+ sessionID: session.sessionID,
1426
+ state: session?.currentState,
1427
+ });
1428
+ }
1429
+
1384
1430
  setLoading(false);
1385
1431
  return {
1386
1432
  dialogState: session.currentState,
@@ -1526,7 +1572,11 @@ const MemoriWidget = ({
1526
1572
  additionalInfoProp?.loginToken ??
1527
1573
  additionalInfo?.loginToken ??
1528
1574
  authToken,
1529
- language: (userLang ?? memori.culture?.split('-')?.[0] ?? 'IT').toLowerCase(),
1575
+ language: (
1576
+ userLang ??
1577
+ memori.culture?.split('-')?.[0] ??
1578
+ 'IT'
1579
+ ).toLowerCase(),
1530
1580
  referral: referral,
1531
1581
  timeZoneOffset: new Date().getTimezoneOffset().toString(),
1532
1582
  },
@@ -1722,7 +1772,11 @@ const MemoriWidget = ({
1722
1772
  loginToken ??
1723
1773
  additionalInfo?.loginToken ??
1724
1774
  authToken,
1725
- language: (userLang ?? memori.culture?.split('-')?.[0] ?? 'IT').toLowerCase(),
1775
+ language: (
1776
+ userLang ??
1777
+ memori.culture?.split('-')?.[0] ??
1778
+ 'IT'
1779
+ ).toLowerCase(),
1726
1780
  referral: referral,
1727
1781
  timeZoneOffset: new Date().getTimezoneOffset().toString(),
1728
1782
  },
@@ -1786,70 +1840,6 @@ const MemoriWidget = ({
1786
1840
  },
1787
1841
  [currentDialogState, memori.needsDateTime, sessionId]
1788
1842
  );
1789
- useEffect(() => {
1790
- if (sessionId && memori.needsDateTime) {
1791
- sendDateChangedEvent({ sessionID: sessionId, state: currentDialogState });
1792
-
1793
- let datePolling: NodeJS.Timeout | null = null;
1794
- let isTabVisible = !document.hidden;
1795
-
1796
- const startDatePolling = () => {
1797
- // stop the polling if it is already running
1798
- if (datePolling) {
1799
- clearInterval(datePolling);
1800
- }
1801
- // start the polling
1802
- datePolling = setInterval(() => {
1803
- if (!document.hidden) {
1804
- sendDateChangedEvent({
1805
- sessionID: sessionId,
1806
- });
1807
- }
1808
- }, 60 * 1000); // 1 minute
1809
- };
1810
-
1811
- const stopDatePolling = () => {
1812
- if (datePolling) {
1813
- clearInterval(datePolling);
1814
- datePolling = null;
1815
- }
1816
- };
1817
-
1818
- const handleVisibilityChange = () => {
1819
- const isVisible = !document.hidden;
1820
-
1821
- if (isVisible && !isTabVisible) {
1822
- // Tab became visible - start polling and send immediate date event
1823
- sendDateChangedEvent({
1824
- sessionID: sessionId,
1825
- state: currentDialogState,
1826
- });
1827
- startDatePolling();
1828
- } else if (!isVisible && isTabVisible) {
1829
- // Tab became hidden - stop polling
1830
- stopDatePolling();
1831
- }
1832
-
1833
- isTabVisible = isVisible;
1834
- };
1835
-
1836
- // Start polling if tab is initially visible
1837
- if (isTabVisible) {
1838
- startDatePolling();
1839
- }
1840
-
1841
- // Add visibility change listener
1842
- document.addEventListener('visibilitychange', handleVisibilityChange);
1843
-
1844
- return () => {
1845
- stopDatePolling();
1846
- document.removeEventListener(
1847
- 'visibilitychange',
1848
- handleVisibilityChange
1849
- );
1850
- };
1851
- }
1852
- }, [memori.needsDateTime, sessionId]);
1853
1843
 
1854
1844
  /**
1855
1845
  * Timeout conversazione
@@ -2443,7 +2433,11 @@ const MemoriWidget = ({
2443
2433
  loginToken ??
2444
2434
  additionalInfo?.loginToken ??
2445
2435
  authToken,
2446
- language: (userLang ?? memori.culture?.split('-')?.[0] ?? 'IT').toLowerCase(),
2436
+ language: (
2437
+ userLang ??
2438
+ memori.culture?.split('-')?.[0] ??
2439
+ 'IT'
2440
+ ).toLowerCase(),
2447
2441
  timeZoneOffset: new Date().getTimezoneOffset().toString(),
2448
2442
  },
2449
2443
  });
@@ -2723,14 +2717,17 @@ const MemoriWidget = ({
2723
2717
 
2724
2718
  // Handle 500 error from TextEnteredEvent
2725
2719
  if (response.resultCode === 500 && response.resultMessage) {
2726
- setHistory(h => [...h, {
2727
- text: 'Error: ' + response.resultMessage,
2728
- emitter: 'system',
2729
- fromUser: false,
2730
- initial: false,
2731
- contextVars: {},
2732
- date: new Date().toISOString(),
2733
- }]);
2720
+ setHistory(h => [
2721
+ ...h,
2722
+ {
2723
+ text: 'Error: ' + response.resultMessage,
2724
+ emitter: 'system',
2725
+ fromUser: false,
2726
+ initial: false,
2727
+ contextVars: {},
2728
+ date: new Date().toISOString(),
2729
+ },
2730
+ ]);
2734
2731
  setMemoriTyping(false);
2735
2732
  return;
2736
2733
  }
@@ -3157,6 +3154,7 @@ const MemoriWidget = ({
3157
3154
  sessionId={sessionId}
3158
3155
  hasUserActivatedSpeak={hasUserActivatedSpeak}
3159
3156
  loading={loading}
3157
+ avatar3dHidden={avatar3dHidden}
3160
3158
  />
3161
3159
 
3162
3160
  <ArtifactAPIBridge
@@ -20,6 +20,7 @@ const WebsiteAssistantLayout: React.FC<LayoutProps> = ({
20
20
  hasUserActivatedSpeak,
21
21
  loading = false,
22
22
  poweredBy,
23
+ avatar3dHidden = false,
23
24
  }) => {
24
25
  const { t } = useTranslation();
25
26
  const [collapsed, _setCollapsed] = useState(true);
@@ -86,28 +87,30 @@ const WebsiteAssistantLayout: React.FC<LayoutProps> = ({
86
87
  />
87
88
  )}
88
89
 
89
- <div className="memori-website_assistant-layout--avatar">
90
- {Avatar && avatarProps && (
91
- <Avatar
92
- {...avatarProps}
93
- integrationConfig={
94
- avatarProps.integrationConfig
95
- ? {
96
- ...avatarProps.integrationConfig,
97
- avatarURL: avatarProps.integrationConfig?.avatarURL
98
- ? `${
99
- avatarProps.integrationConfig?.avatarURL.split(
100
- '#'
101
- )[0]
102
- }#${expandedKey}`
103
- : undefined,
104
- }
105
- : {}
106
- }
107
- key={expandedKey}
108
- />
109
- )}
110
- </div>
90
+ {!avatar3dHidden && (
91
+ <div className="memori-website_assistant-layout--avatar">
92
+ {Avatar && avatarProps && (
93
+ <Avatar
94
+ {...avatarProps}
95
+ integrationConfig={
96
+ avatarProps.integrationConfig
97
+ ? {
98
+ ...avatarProps.integrationConfig,
99
+ avatarURL: avatarProps.integrationConfig?.avatarURL
100
+ ? `${
101
+ avatarProps.integrationConfig?.avatarURL.split(
102
+ '#'
103
+ )[0]
104
+ }#${expandedKey}`
105
+ : undefined,
106
+ }
107
+ : {}
108
+ }
109
+ key={expandedKey}
110
+ />
111
+ )}
112
+ </div>
113
+ )}
111
114
 
112
115
  <div id="extension" />
113
116
 
@@ -50,6 +50,7 @@ DefaultLayout.args = {
50
50
  showUpload: true,
51
51
  showReasoning: false,
52
52
  showLogin: true,
53
+ multilingual: true,
53
54
  };
54
55
 
55
56
 
@@ -207,6 +208,26 @@ WebsiteAssistant.args = {
207
208
  },
208
209
  };
209
210
 
211
+ export const WebsiteAssistantWithout3DAvatar = Template.bind({});
212
+ WebsiteAssistantWithout3DAvatar.args = {
213
+ ...WebsiteAssistant.args,
214
+ integration: {
215
+ ...integration,
216
+ customData: JSON.stringify({
217
+ textColor: '#2a2a2a',
218
+ buttonBgColor: '#653165',
219
+ buttonTextColor: '#ffffff',
220
+ blurBackground: true,
221
+ innerBgColor: 'light',
222
+ innerBgAlpha: 0.8,
223
+ multilanguage: true,
224
+ avatar: 'readyplayerme',
225
+ avatarURL: 'https://assets.memori.ai/api/v2/asset/b791f77c-1a94-4272-829e-eca82fcc62b7.glb',
226
+ avatar_3d_hidden: true,
227
+ }),
228
+ },
229
+ };
230
+
210
231
  export const HiddenChat = Template.bind({});
211
232
  HiddenChat.args = {
212
233
  ...DefaultLayout.args,
package/src/index.tsx CHANGED
@@ -346,6 +346,8 @@ const Memori: React.FC<Props> = ({
346
346
  | undefined,
347
347
  showLogin: memori?.enableDeepThought,
348
348
  memoriLang: memori?.culture?.split('-')?.[0],
349
+ uiLang,
350
+ spokenLang,
349
351
  autoStart:
350
352
  layout === 'HIDDEN_CHAT'
351
353
  ? true
@@ -383,7 +385,9 @@ const Memori: React.FC<Props> = ({
383
385
  enableAudio,
384
386
  defaultSpeakerActive,
385
387
  useMathFormatting,
386
- memoriLang: uiLang ?? spokenLang ?? memori?.culture?.split('-')?.[0],
388
+ memoriLang: memori?.culture?.split('-')?.[0],
389
+ uiLang,
390
+ spokenLang,
387
391
  };
388
392
 
389
393
  const [pulseSent, setPulseSent] = useState(false);
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
- export const version = '8.21.0';
2
+ export const version = '8.23.0';