@memori.ai/memori-react 7.7.0 → 7.8.0-rc.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 (178) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/components/Avatar/Avatar.js +2 -2
  3. package/dist/components/Avatar/Avatar.js.map +1 -1
  4. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.d.ts +2 -0
  5. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +2 -2
  6. package/dist/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  7. package/dist/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.d.ts +2 -0
  8. package/dist/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js +1 -1
  9. package/dist/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js.map +1 -1
  10. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.d.ts +3 -1
  11. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js +77 -50
  12. package/dist/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js.map +1 -1
  13. package/dist/components/Avatar/AvatarView/index.d.ts +3 -1
  14. package/dist/components/Avatar/AvatarView/index.js +2 -2
  15. package/dist/components/Avatar/AvatarView/index.js.map +1 -1
  16. package/dist/components/MemoriWidget/MemoriWidget.js +127 -113
  17. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  18. package/dist/components/layouts/ZoomedFullBody.js +1 -8
  19. package/dist/components/layouts/ZoomedFullBody.js.map +1 -1
  20. package/dist/components/layouts/zoomed-full-body.css +6 -2
  21. package/dist/context/visemeContext.d.ts +1 -0
  22. package/dist/context/visemeContext.js +19 -9
  23. package/dist/context/visemeContext.js.map +1 -1
  24. package/dist/styles.css +0 -1
  25. package/esm/components/Avatar/Avatar.js +2 -2
  26. package/esm/components/Avatar/Avatar.js.map +1 -1
  27. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.d.ts +2 -0
  28. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js +2 -2
  29. package/esm/components/Avatar/AvatarView/AvatarComponent/avatarComponent.js.map +1 -1
  30. package/esm/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.d.ts +2 -0
  31. package/esm/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js +1 -1
  32. package/esm/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.js.map +1 -1
  33. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.d.ts +3 -1
  34. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js +81 -53
  35. package/esm/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.js.map +1 -1
  36. package/esm/components/Avatar/AvatarView/index.d.ts +3 -1
  37. package/esm/components/Avatar/AvatarView/index.js +2 -2
  38. package/esm/components/Avatar/AvatarView/index.js.map +1 -1
  39. package/esm/components/MemoriWidget/MemoriWidget.js +127 -113
  40. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  41. package/esm/components/layouts/ZoomedFullBody.js +1 -8
  42. package/esm/components/layouts/ZoomedFullBody.js.map +1 -1
  43. package/esm/components/layouts/zoomed-full-body.css +6 -2
  44. package/esm/context/visemeContext.d.ts +1 -0
  45. package/esm/context/visemeContext.js +20 -10
  46. package/esm/context/visemeContext.js.map +1 -1
  47. package/esm/styles.css +0 -1
  48. package/package.json +1 -1
  49. package/src/components/Avatar/Avatar.stories.tsx +5 -7
  50. package/src/components/Avatar/Avatar.tsx +3 -1
  51. package/src/components/Avatar/AvatarView/AvatarComponent/avatarComponent.tsx +13 -0
  52. package/src/components/Avatar/AvatarView/AvatarComponent/components/fullbodyAvatar.tsx +3 -1
  53. package/src/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.tsx +116 -65
  54. package/src/components/Avatar/AvatarView/AvatarView.stories.tsx +12 -36
  55. package/src/components/Avatar/AvatarView/index.tsx +6 -0
  56. package/src/components/MemoriWidget/MemoriWidget.tsx +164 -136
  57. package/src/components/layouts/ZoomedFullBody.test.tsx +1 -1
  58. package/src/components/layouts/ZoomedFullBody.tsx +4 -9
  59. package/src/components/layouts/zoomed-full-body.css +6 -2
  60. package/src/context/visemeContext.tsx +104 -70
  61. package/src/index.stories.tsx +23 -21
  62. package/src/styles.css +0 -1
  63. package/dist/components/AttachmentMediaModal/AttachmentMediaModal.d.ts +0 -14
  64. package/dist/components/AttachmentMediaModal/AttachmentMediaModal.js +0 -66
  65. package/dist/components/AttachmentMediaModal/AttachmentMediaModal.js.map +0 -1
  66. package/dist/components/Avatar/AvatarView/components/controls.d.ts +0 -23
  67. package/dist/components/Avatar/AvatarView/components/controls.js +0 -55
  68. package/dist/components/Avatar/AvatarView/components/controls.js.map +0 -1
  69. package/dist/components/Avatar/AvatarView/components/fullbodyAvatar.d.ts +0 -19
  70. package/dist/components/Avatar/AvatarView/components/fullbodyAvatar.js +0 -61
  71. package/dist/components/Avatar/AvatarView/components/fullbodyAvatar.js.map +0 -1
  72. package/dist/components/Avatar/AvatarView/components/halfbodyAvatar.d.ts +0 -9
  73. package/dist/components/Avatar/AvatarView/components/halfbodyAvatar.js +0 -39
  74. package/dist/components/Avatar/AvatarView/components/halfbodyAvatar.js.map +0 -1
  75. package/dist/components/Avatar/AvatarView/components/loader.d.ts +0 -5
  76. package/dist/components/Avatar/AvatarView/components/loader.js +0 -12
  77. package/dist/components/Avatar/AvatarView/components/loader.js.map +0 -1
  78. package/dist/components/Avatar/AvatarView/utils/useMouthSpeaking.d.ts +0 -2
  79. package/dist/components/Avatar/AvatarView/utils/useMouthSpeaking.js +0 -68
  80. package/dist/components/Avatar/AvatarView/utils/useMouthSpeaking.js.map +0 -1
  81. package/dist/components/AvatarView/components/avatar.d.ts +0 -9
  82. package/dist/components/AvatarView/components/avatar.js +0 -35
  83. package/dist/components/AvatarView/components/avatar.js.map +0 -1
  84. package/dist/components/AvatarView/components/fullbodyAvatar.d.ts +0 -10
  85. package/dist/components/AvatarView/components/fullbodyAvatar.js +0 -62
  86. package/dist/components/AvatarView/components/fullbodyAvatar.js.map +0 -1
  87. package/dist/components/AvatarView/components/loader.d.ts +0 -5
  88. package/dist/components/AvatarView/components/loader.js +0 -12
  89. package/dist/components/AvatarView/components/loader.js.map +0 -1
  90. package/dist/components/AvatarView/index.d.ts +0 -17
  91. package/dist/components/AvatarView/index.js +0 -35
  92. package/dist/components/AvatarView/index.js.map +0 -1
  93. package/dist/components/AvatarView/utils/useEyeBlink.d.ts +0 -2
  94. package/dist/components/AvatarView/utils/useEyeBlink.js +0 -40
  95. package/dist/components/AvatarView/utils/useEyeBlink.js.map +0 -1
  96. package/dist/components/AvatarView/utils/useHeadMovement.d.ts +0 -2
  97. package/dist/components/AvatarView/utils/useHeadMovement.js +0 -53
  98. package/dist/components/AvatarView/utils/useHeadMovement.js.map +0 -1
  99. package/dist/components/AvatarView/utils/useLoadingMorphAnim.d.ts +0 -2
  100. package/dist/components/AvatarView/utils/useLoadingMorphAnim.js +0 -34
  101. package/dist/components/AvatarView/utils/useLoadingMorphAnim.js.map +0 -1
  102. package/dist/components/AvatarView/utils/useMouthSpeaking.d.ts +0 -2
  103. package/dist/components/AvatarView/utils/useMouthSpeaking.js +0 -60
  104. package/dist/components/AvatarView/utils/useMouthSpeaking.js.map +0 -1
  105. package/dist/components/AvatarView/utils/useSmile.d.ts +0 -2
  106. package/dist/components/AvatarView/utils/useSmile.js +0 -30
  107. package/dist/components/AvatarView/utils/useSmile.js.map +0 -1
  108. package/dist/components/AvatarView/utils/utils.d.ts +0 -13
  109. package/dist/components/AvatarView/utils/utils.js +0 -42
  110. package/dist/components/AvatarView/utils/utils.js.map +0 -1
  111. package/dist/components/ImageUpload/ImageUpload.css +0 -168
  112. package/dist/components/ImageUpload/ImageUpload.d.ts +0 -28
  113. package/dist/components/ImageUpload/ImageUpload.js +0 -163
  114. package/dist/components/ImageUpload/ImageUpload.js.map +0 -1
  115. package/dist/components/layouts/Default.d.ts +0 -17
  116. package/dist/components/layouts/Default.js +0 -8
  117. package/dist/components/layouts/Default.js.map +0 -1
  118. package/dist/components/ui/Message.d.ts +0 -17
  119. package/dist/components/ui/Message.js +0 -13
  120. package/dist/components/ui/Message.js.map +0 -1
  121. package/esm/components/AttachmentMediaModal/AttachmentMediaModal.d.ts +0 -14
  122. package/esm/components/AttachmentMediaModal/AttachmentMediaModal.js +0 -63
  123. package/esm/components/AttachmentMediaModal/AttachmentMediaModal.js.map +0 -1
  124. package/esm/components/Avatar/AvatarView/components/controls.d.ts +0 -23
  125. package/esm/components/Avatar/AvatarView/components/controls.js +0 -52
  126. package/esm/components/Avatar/AvatarView/components/controls.js.map +0 -1
  127. package/esm/components/Avatar/AvatarView/components/fullbodyAvatar.d.ts +0 -19
  128. package/esm/components/Avatar/AvatarView/components/fullbodyAvatar.js +0 -57
  129. package/esm/components/Avatar/AvatarView/components/fullbodyAvatar.js.map +0 -1
  130. package/esm/components/Avatar/AvatarView/components/halfbodyAvatar.d.ts +0 -9
  131. package/esm/components/Avatar/AvatarView/components/halfbodyAvatar.js +0 -35
  132. package/esm/components/Avatar/AvatarView/components/halfbodyAvatar.js.map +0 -1
  133. package/esm/components/Avatar/AvatarView/components/loader.d.ts +0 -5
  134. package/esm/components/Avatar/AvatarView/components/loader.js +0 -9
  135. package/esm/components/Avatar/AvatarView/components/loader.js.map +0 -1
  136. package/esm/components/Avatar/AvatarView/utils/useMouthSpeaking.d.ts +0 -2
  137. package/esm/components/Avatar/AvatarView/utils/useMouthSpeaking.js +0 -65
  138. package/esm/components/Avatar/AvatarView/utils/useMouthSpeaking.js.map +0 -1
  139. package/esm/components/AvatarView/components/avatar.d.ts +0 -9
  140. package/esm/components/AvatarView/components/avatar.js +0 -31
  141. package/esm/components/AvatarView/components/avatar.js.map +0 -1
  142. package/esm/components/AvatarView/components/fullbodyAvatar.d.ts +0 -10
  143. package/esm/components/AvatarView/components/fullbodyAvatar.js +0 -58
  144. package/esm/components/AvatarView/components/fullbodyAvatar.js.map +0 -1
  145. package/esm/components/AvatarView/components/loader.d.ts +0 -5
  146. package/esm/components/AvatarView/components/loader.js +0 -9
  147. package/esm/components/AvatarView/components/loader.js.map +0 -1
  148. package/esm/components/AvatarView/index.d.ts +0 -17
  149. package/esm/components/AvatarView/index.js +0 -31
  150. package/esm/components/AvatarView/index.js.map +0 -1
  151. package/esm/components/AvatarView/utils/useEyeBlink.d.ts +0 -2
  152. package/esm/components/AvatarView/utils/useEyeBlink.js +0 -37
  153. package/esm/components/AvatarView/utils/useEyeBlink.js.map +0 -1
  154. package/esm/components/AvatarView/utils/useHeadMovement.d.ts +0 -2
  155. package/esm/components/AvatarView/utils/useHeadMovement.js +0 -50
  156. package/esm/components/AvatarView/utils/useHeadMovement.js.map +0 -1
  157. package/esm/components/AvatarView/utils/useLoadingMorphAnim.d.ts +0 -2
  158. package/esm/components/AvatarView/utils/useLoadingMorphAnim.js +0 -31
  159. package/esm/components/AvatarView/utils/useLoadingMorphAnim.js.map +0 -1
  160. package/esm/components/AvatarView/utils/useMouthSpeaking.d.ts +0 -2
  161. package/esm/components/AvatarView/utils/useMouthSpeaking.js +0 -57
  162. package/esm/components/AvatarView/utils/useMouthSpeaking.js.map +0 -1
  163. package/esm/components/AvatarView/utils/useSmile.d.ts +0 -2
  164. package/esm/components/AvatarView/utils/useSmile.js +0 -27
  165. package/esm/components/AvatarView/utils/useSmile.js.map +0 -1
  166. package/esm/components/AvatarView/utils/utils.d.ts +0 -13
  167. package/esm/components/AvatarView/utils/utils.js +0 -33
  168. package/esm/components/AvatarView/utils/utils.js.map +0 -1
  169. package/esm/components/ImageUpload/ImageUpload.css +0 -168
  170. package/esm/components/ImageUpload/ImageUpload.d.ts +0 -28
  171. package/esm/components/ImageUpload/ImageUpload.js +0 -160
  172. package/esm/components/ImageUpload/ImageUpload.js.map +0 -1
  173. package/esm/components/layouts/Default.d.ts +0 -17
  174. package/esm/components/layouts/Default.js +0 -5
  175. package/esm/components/layouts/Default.js.map +0 -1
  176. package/esm/components/ui/Message.d.ts +0 -17
  177. package/esm/components/ui/Message.js +0 -10
  178. package/esm/components/ui/Message.js.map +0 -1
@@ -543,19 +543,14 @@ const MemoriWidget = ({
543
543
  const [hideEmissions, setHideEmissions] = useState(false);
544
544
 
545
545
  const {
546
+ updateCurrentViseme,
546
547
  startProcessing,
547
- stopProcessing,
548
+ resetAndStartProcessing,
549
+ isProcessing,
548
550
  addViseme,
551
+ stopProcessing,
549
552
  resetVisemeQueue,
550
- isProcessing,
551
553
  } = useViseme();
552
- const audioContextRef = useRef<AudioContext | null>(null);
553
- const speechSynthesizerRef = useRef<speechSdk.SpeechSynthesizer | null>(null);
554
- const audioDestinationRef = useRef<speechSdk.SpeakerAudioDestination | null>(
555
- null
556
- );
557
- const currentSpeechRef = useRef<{ cancel: () => void } | null>(null);
558
-
559
554
 
560
555
  useEffect(() => {
561
556
  setIsPlayingAudio(!!speechSynthesizer);
@@ -1890,167 +1885,204 @@ const MemoriWidget = ({
1890
1885
  const e = new CustomEvent('MemoriEndSpeak');
1891
1886
  document.dispatchEvent(e);
1892
1887
  };
1893
- const initializeAudioContext = useCallback(() => {
1894
- if (!audioContextRef.current || audioContextRef.current.state === 'closed') {
1895
- audioContextRef.current = new (window.AudioContext || (window as any).webkitAudioContext)() as unknown as IAudioContext;
1896
- }
1897
- return audioContextRef.current;
1898
- }, []);
1899
-
1900
- const initializeSpeechSynthesizer = useCallback((audioConfig: speechSdk.AudioConfig) => {
1901
- if (!speechSynthesizerRef.current && AZURE_COGNITIVE_SERVICES_TTS_KEY) {
1902
- const speechConfig = speechSdk.SpeechConfig.fromSubscription(
1903
- AZURE_COGNITIVE_SERVICES_TTS_KEY,
1904
- 'eastus'
1905
- );
1906
- speechSynthesizerRef.current = new speechSdk.SpeechSynthesizer(speechConfig, audioConfig);
1907
- }
1908
- return speechSynthesizerRef.current;
1909
- }, []);
1910
-
1911
- const stopCurrentSpeech = useCallback(() => {
1912
- if (currentSpeechRef.current) {
1913
- currentSpeechRef.current.cancel();
1914
- currentSpeechRef.current = null;
1915
- }
1916
- if (audioContextRef.current) {
1917
- audioContextRef.current.suspend();
1918
- }
1919
- if (audioDestinationRef.current) {
1920
- audioDestinationRef.current.pause();
1921
- }
1922
- setIsPlayingAudio(false);
1923
- stopProcessing();
1924
- resetVisemeQueue();
1925
- }, []);
1926
1888
 
1927
- const speak = useCallback(async (text: string): Promise<void> => {
1889
+ const speak = (text: string): void => {
1928
1890
  if (!AZURE_COGNITIVE_SERVICES_TTS_KEY || preview) {
1929
1891
  emitEndSpeakEvent();
1930
1892
  return;
1931
1893
  }
1932
-
1933
1894
  stopListening();
1934
- stopCurrentSpeech(); // Stop any ongoing speech
1895
+ // stopAudio();
1935
1896
 
1936
- if (preview || muteSpeaker || speakerMuted) {
1937
- setIsPlayingAudio(false);
1897
+ if (preview) return;
1898
+
1899
+ if (muteSpeaker || speakerMuted) {
1900
+ memoriSpeaking = false;
1938
1901
  setMemoriTyping(false);
1902
+
1939
1903
  emitEndSpeakEvent();
1904
+
1905
+ // trigger start continuous listening if set, see MemoriChat
1940
1906
  if (continuousSpeech) {
1941
1907
  setListeningTimeout();
1942
1908
  }
1943
1909
  return;
1944
1910
  }
1945
1911
 
1946
- try {
1947
- const audioContext = initializeAudioContext();
1948
- await audioContext.resume();
1912
+ if (audioDestination) audioDestination.pause();
1949
1913
 
1950
- if (!audioDestinationRef.current) {
1951
- audioDestinationRef.current = new speechSdk.SpeakerAudioDestination();
1914
+ let isSafari =
1915
+ window.navigator.userAgent.includes('Safari') &&
1916
+ !window.navigator.userAgent.includes('Chrome');
1917
+ let isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
1918
+ if ((audioContext.state as string) === 'interrupted') {
1919
+ audioContext.resume().then(() => speak(text));
1920
+ return;
1921
+ }
1922
+ if (audioContext.state === 'closed') {
1923
+ audioContext = new AudioContext();
1924
+ let buffer = audioContext.createBuffer(1, 10000, 22050);
1925
+ let source = audioContext.createBufferSource();
1926
+ source.buffer = buffer;
1927
+ source.connect(audioContext.destination);
1928
+ } else if (audioContext.state === 'suspended') {
1929
+ stopAudio();
1930
+
1931
+ audioContext = new AudioContext();
1932
+ let buffer = audioContext.createBuffer(1, 10000, 22050);
1933
+ let source = audioContext.createBufferSource();
1934
+ source.buffer = buffer;
1935
+ source.connect(audioContext.destination);
1936
+ }
1937
+
1938
+ if (!speechSynthesizer) {
1939
+ if (!isIOS) {
1940
+ audioDestination = new speechSdk.SpeakerAudioDestination();
1952
1941
  }
1942
+ let audioConfig =
1943
+ speechSdk.AudioConfig.fromSpeakerOutput(audioDestination);
1944
+ speechSynthesizer = new speechSdk.SpeechSynthesizer(
1945
+ speechConfig,
1946
+ audioConfig
1947
+ );
1948
+ }
1953
1949
 
1954
- const audioConfig = speechSdk.AudioConfig.fromSpeakerOutput(audioDestinationRef.current);
1955
- const speechSynthesizer = initializeSpeechSynthesizer(audioConfig);
1950
+ const source = audioContext.createBufferSource();
1951
+ source.addEventListener('ended', () => {
1952
+ setIsPlayingAudio(false);
1953
+ memoriSpeaking = false;
1954
+ });
1955
+ audioDestination.onAudioEnd = () => {
1956
+ setIsPlayingAudio(false);
1957
+ memoriSpeaking = false;
1958
+ source.disconnect();
1956
1959
 
1957
- if (speechSynthesizer) {
1960
+ emitEndSpeakEvent();
1958
1961
 
1959
- // Add the new visemeReceived event listener
1960
- speechSynthesizer.visemeReceived = (_, e) => {
1961
- addViseme(e.visemeId, e.audioOffset);
1962
- console.log('viseme added')
1963
- };
1964
- }
1965
- startProcessing();
1966
-
1967
- const textToSpeak = escapeHTML(stripMarkdown(stripEmojis(stripHTML(stripOutputTags(text)))));
1968
-
1969
- const ssml = `
1970
- <speak version="1.0"
1971
- xmlns="http://www.w3.org/2001/10/synthesis"
1972
- xmlns:mstts="https://www.w3.org/2001/mstts"
1973
- xml:lang="${getCultureCodeByLanguage(userLang)}">
1974
- <voice name="${getTTSVoice(userLang)}">
1975
- <s>${replaceTextWithPhonemes(textToSpeak, userLang.toLowerCase())}</s>
1976
- </voice>
1977
- </speak>
1978
- `;
1979
-
1980
- const speakPromise = new Promise<speechSdk.SpeechSynthesisResult>((resolve, reject) => {
1981
- speechSynthesizer?.speakSsmlAsync(
1982
- ssml,
1983
- result => resolve(result),
1984
- error => reject(error)
1985
- );
1986
- });
1962
+ // trigger start continuous listening if set
1963
+ onEndSpeakStartListen();
1964
+ };
1987
1965
 
1988
- currentSpeechRef.current = {
1989
- cancel: () => {
1990
- speechSynthesizer?.close();
1991
- audioDestinationRef.current?.pause();
1992
- }
1993
- };
1966
+ // Add this before starting new speech synthesis
1967
+ resetVisemeQueue();
1994
1968
 
1995
- const result = await speakPromise;
1969
+ // Set up the viseme event handler
1970
+ speechSynthesizer.visemeReceived = function (_, e) {
1971
+ addViseme(e.visemeId, e.audioOffset);
1972
+ };
1996
1973
 
1997
- setIsPlayingAudio(true);
1974
+ const textToSpeak = escapeHTML(
1975
+ stripMarkdown(stripEmojis(stripHTML(stripOutputTags(text))))
1976
+ );
1998
1977
 
1999
- if (audioContext && result) {
2000
- const audioBuffer = await audioContext.decodeAudioData(result.audioData);
2001
- const source = audioContext.createBufferSource();
2002
- source.buffer = audioBuffer;
2003
- source.connect(audioContext.destination);
1978
+ speechSynthesizer.speakSsmlAsync(
1979
+ `<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" xml:lang="${getCultureCodeByLanguage(
1980
+ userLang
1981
+ )}"><voice name="${getTTSVoice(userLang)}"><s>${replaceTextWithPhonemes(
1982
+ textToSpeak,
1983
+ userLang.toLowerCase()
1984
+ )}</s></voice></speak>`,
1985
+ result => {
1986
+ if (result) {
1987
+ setIsPlayingAudio(true);
1988
+ memoriSpeaking = true;
2004
1989
 
2005
- source.onended = () => {
2006
- setIsPlayingAudio(false);
1990
+ try {
1991
+ // Decode the audio data
1992
+ audioContext!.decodeAudioData(result.audioData, function (buffer) {
1993
+ console.log('decoded');
1994
+ const currentSource = audioContext!.createBufferSource();
1995
+ currentSource.buffer = buffer;
1996
+ currentSource.connect(audioContext!.destination);
1997
+ startProcessing();
1998
+ currentSource.start();
1999
+
2000
+ currentSource.onended = () => {
2001
+ console.log('ended');
2002
+ setIsPlayingAudio(false);
2003
+ memoriSpeaking = false;
2004
+ stopProcessing();
2005
+ resetVisemeQueue();
2006
+ emitEndSpeakEvent();
2007
+ };
2008
+ });
2009
+
2010
+ // Handle the audio context state changes
2011
+ audioContext.onstatechange = () => {
2012
+ if (
2013
+ audioContext.state === 'suspended' ||
2014
+ audioContext.state === 'closed'
2015
+ ) {
2016
+ console.log('suspended');
2017
+ source.disconnect();
2018
+ setIsPlayingAudio(false);
2019
+ stopProcessing();
2020
+ resetVisemeQueue();
2021
+ memoriSpeaking = false;
2022
+ emitEndSpeakEvent();
2023
+ } else if ((audioContext.state as string) === 'interrupted') {
2024
+ console.log('interrupted');
2025
+ stopProcessing();
2026
+ resetVisemeQueue();
2027
+
2028
+ audioContext.resume();
2029
+ }
2030
+ };
2031
+ } catch (e) {
2032
+ console.warn('speak error: ', e);
2033
+ window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
2034
+ setIsPlayingAudio(false);
2035
+ memoriSpeaking = false;
2036
+ stopProcessing();
2037
+ resetVisemeQueue();
2038
+
2039
+ if (speechSynthesizer) {
2040
+ speechSynthesizer.close();
2041
+ speechSynthesizer = null;
2042
+ }
2043
+ emitEndSpeakEvent();
2044
+ }
2045
+ } else {
2046
+ audioContext.resume();
2007
2047
  stopProcessing();
2008
2048
  resetVisemeQueue();
2009
- currentSpeechRef.current = null;
2049
+ setIsPlayingAudio(false);
2050
+ memoriSpeaking = false;
2010
2051
  emitEndSpeakEvent();
2011
- onEndSpeakStartListen();
2012
- };
2013
-
2014
- await audioContext.resume();
2015
- source.start(0);
2016
- } else {
2052
+ }
2053
+ },
2054
+ error => {
2055
+ console.error('speak:', error);
2056
+ window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
2057
+ setIsPlayingAudio(false);
2058
+ memoriSpeaking = false;
2017
2059
  stopProcessing();
2018
2060
  resetVisemeQueue();
2019
- throw new Error('No result from speech synthesis');
2061
+ emitEndSpeakEvent();
2020
2062
  }
2021
- } catch (error) {
2022
- console.error('Speech synthesis error:', error);
2023
- stopProcessing();
2024
- resetVisemeQueue();
2025
- // Fallback to browser's speech synthesis
2026
- const utterance = new SpeechSynthesisUtterance(text);
2027
- window.speechSynthesis.speak(utterance);
2028
- } finally {
2029
- setMemoriTyping(false);
2030
- }
2031
- }, [initializeAudioContext, initializeSpeechSynthesizer, stopCurrentSpeech]);
2063
+ );
2032
2064
 
2033
- const stopAudio = useCallback(() => {
2065
+ setMemoriTyping(false);
2066
+ };
2067
+ const stopAudio = () => {
2034
2068
  setIsPlayingAudio(false);
2035
2069
  memoriSpeaking = false;
2036
2070
  try {
2037
- if (speechSynthesizerRef.current) {
2038
- speechSynthesizerRef.current.close();
2039
- speechSynthesizerRef.current = null;
2071
+ if (speechSynthesizer) {
2072
+ speechSynthesizer.close();
2073
+ speechSynthesizer = null;
2040
2074
  }
2041
- if (audioContextRef.current && audioContextRef.current.state !== 'closed') {
2042
- audioContextRef.current.close();
2075
+ if (audioContext.state !== 'closed') {
2076
+ audioContext.close();
2043
2077
  }
2044
- if (audioDestinationRef.current) {
2045
- audioDestinationRef.current.pause();
2046
- audioDestinationRef.current.close();
2078
+ if (audioDestination) {
2079
+ audioDestination.pause();
2080
+ audioDestination.close();
2047
2081
  }
2048
- stopCurrentSpeech();
2049
2082
  } catch (e) {
2050
2083
  console.debug('stopAudio error: ', e);
2051
2084
  }
2052
- }, [stopCurrentSpeech]);
2053
-
2085
+ };
2054
2086
 
2055
2087
  /**
2056
2088
  * Focus on the chat input on mount
@@ -2214,12 +2246,6 @@ const MemoriWidget = ({
2214
2246
  useEffect(() => {
2215
2247
  return () => {
2216
2248
  resetUIEffects();
2217
- if (speechSynthesizerRef.current) {
2218
- speechSynthesizerRef.current.close();
2219
- }
2220
- if (audioContextRef.current) {
2221
- audioContextRef.current.close();
2222
- }
2223
2249
  };
2224
2250
  // eslint-disable-next-line react-hooks/exhaustive-deps
2225
2251
  }, []);
@@ -2973,7 +2999,9 @@ const MemoriWidget = ({
2973
2999
 
2974
3000
  const showFullHistory =
2975
3001
  showOnlyLastMessages === undefined
2976
- ? layout !== 'TOTEM' && layout !== 'WEBSITE_ASSISTANT'
3002
+ ? layout !== 'TOTEM' &&
3003
+ layout !== 'WEBSITE_ASSISTANT' &&
3004
+ layout !== 'HIDDEN_CHAT'
2977
3005
  : !showOnlyLastMessages;
2978
3006
 
2979
3007
  const headerProps: HeaderProps = {
@@ -34,4 +34,4 @@ it('renders ZOOMED_FULL_BODY layout unchanged', () => {
34
34
  </I18nWrapper>
35
35
  );
36
36
  expect(container).toMatchSnapshot();
37
- });
37
+ });
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useCallback } from 'react';
1
+ import React from 'react';
2
2
  import Spin from '../ui/Spin';
3
3
  import { LayoutProps } from '../MemoriWidget/MemoriWidget';
4
4
 
@@ -21,20 +21,15 @@ const ZoomedFullBodyLayout: React.FC<LayoutProps> = ({
21
21
  loading = false,
22
22
  poweredBy,
23
23
  }) => {
24
- useEffect(() => {
25
- document.body.style.overflow = 'hidden';
26
- return () => {
27
- document.body.style.overflow = '';
28
- };
29
- }, []);
30
-
31
24
  return (
32
25
  <>
33
26
  {integrationStyle}
34
27
  {integrationBackground}
35
28
 
36
29
  <Spin className="memori-spin--zoomed-full-body" spinning={loading}>
37
- {showInstruct && ChangeMode && changeModeProps && <ChangeMode {...changeModeProps} />}
30
+ {showInstruct && ChangeMode && changeModeProps && (
31
+ <ChangeMode {...changeModeProps} />
32
+ )}
38
33
 
39
34
  {Header && headerProps && <Header {...headerProps} />}
40
35
 
@@ -1,5 +1,5 @@
1
1
  .memori-spin--zoomed-full-body {
2
- height: calc(100vh) !important;
2
+ height: 100vh !important;
3
3
  }
4
4
 
5
5
  .memori--powered-by-custom {
@@ -13,4 +13,8 @@
13
13
 
14
14
  .memori--grid-column--zoomed-full-body {
15
15
  max-height: calc(100% - 80px) !important;
16
- }
16
+ }
17
+
18
+ .memori.memori-widget.memori-layout-zoomed_full_body .memori--grid {
19
+ height: calc(100% - 25px);
20
+ }
@@ -1,9 +1,16 @@
1
- import React, { createContext, useContext, useRef, useCallback, useState, useEffect } from 'react';
2
-
3
- type Viseme = {
4
- name: string;
5
- weight: number;
6
- startTime: number;
1
+ import React, {
2
+ createContext,
3
+ useContext,
4
+ useRef,
5
+ useCallback,
6
+ useState,
7
+ useEffect,
8
+ } from 'react';
9
+
10
+ type Viseme = {
11
+ name: string;
12
+ weight: number;
13
+ startTime: number;
7
14
  endTime: number;
8
15
  };
9
16
 
@@ -13,6 +20,7 @@ interface VisemeContextType {
13
20
  startProcessing: () => void;
14
21
  stopProcessing: () => void;
15
22
  resetVisemeQueue: () => void;
23
+ resetAndStartProcessing: () => void;
16
24
  isProcessing: boolean;
17
25
  }
18
26
 
@@ -44,80 +52,98 @@ const VISEME_MAP: { [key: number]: string } = {
44
52
  21: 'viseme_PP', // y (closest match, could be debated)
45
53
  };
46
54
 
47
- const DEFAULT_VISEME_DURATION = 0.04 //0; // Reduced from 0.4 for smoother transitions
48
- const VISEME_OVERLAP = 0.35; // Slightly increased from 0.04 for more overlap
49
- const SMOOTHING_FACTOR = 0.35 // New constant for weight smoothing
50
- const TIME_OFFSET =-0.15; // Adjust this value as needed (in seconds)
51
- const PRELOAD_TIME = 0.515; // Preload visemes 0.5 seconds in advance
55
+ const DEFAULT_VISEME_DURATION = 0.04; //0; // Reduced from 0.4 for smoother transitions
56
+ const VISEME_OVERLAP = 0.15; // Slightly increased from 0.04 for more overlap
57
+ const SMOOTHING_FACTOR = 0.35; // New constant for weight smoothing
58
+ const TIME_OFFSET = -0.04; // Adjust this value as needed (in seconds)
59
+ const PRELOAD_TIME = 1; // Preload visemes 0.5 seconds in advance
52
60
 
53
- export const VisemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
61
+ export const VisemeProvider: React.FC<{ children: React.ReactNode }> = ({
62
+ children,
63
+ }) => {
54
64
  const visemeQueueRef = useRef<Viseme[]>([]);
55
65
  const startTimeRef = useRef<number | null>(null);
56
66
  const [isProcessing, setIsProcessing] = useState(false);
57
67
  const lastVisemeRef = useRef<Viseme | null>(null);
58
68
 
59
- const addViseme = useCallback((visemeId: number, audioOffset: number) => {
60
- const visemeName = VISEME_MAP[visemeId] || 'viseme_sil';
61
- const startTime = audioOffset / 10000000 + TIME_OFFSET;
62
- const endTime = startTime + DEFAULT_VISEME_DURATION;
63
- const newViseme: Viseme = {
64
- name: visemeName,
65
- weight: 0,
66
- startTime,
67
- endTime,
68
- };
69
- visemeQueueRef.current.push(newViseme);
70
-
71
- if (!isProcessing) {
72
- startProcessing();
73
- }
74
- }, [isProcessing]);
69
+ const addViseme = useCallback(
70
+ (visemeId: number, audioOffset: number) => {
71
+ const visemeName = VISEME_MAP[visemeId] || 'viseme_sil';
72
+ const startTime = audioOffset / 10000000 + TIME_OFFSET;
73
+ const endTime = startTime + DEFAULT_VISEME_DURATION;
74
+ const newViseme: Viseme = {
75
+ name: visemeName,
76
+ weight: 0,
77
+ startTime,
78
+ endTime,
79
+ };
80
+ visemeQueueRef.current.push(newViseme);
81
+
82
+ if (!isProcessing) {
83
+ startProcessing();
84
+ }
85
+ },
86
+ [isProcessing]
87
+ );
75
88
 
76
- const updateCurrentViseme = useCallback((currentTime: number): Viseme | null => {
77
- if (!isProcessing || startTimeRef.current === null) {
78
- console.log('StartTimeRef not set');
79
- return null;
80
- }
81
-
82
- const elapsedTime = currentTime - startTimeRef.current + PRELOAD_TIME
83
-
84
- // Remove expired visemes
85
- visemeQueueRef.current = visemeQueueRef.current.filter(v => v.endTime > elapsedTime);
86
-
87
- const currentViseme = visemeQueueRef.current.find(v =>
88
- v.startTime <= elapsedTime && v.endTime > elapsedTime - VISEME_OVERLAP
89
- );
90
-
91
- if (currentViseme) {
92
- console.log('CurrentViseme Found!')
93
- const visemeProgress = (elapsedTime - currentViseme.startTime) / (currentViseme.endTime - currentViseme.startTime);
94
- const targetWeight = Math.sin(Math.PI * Math.min(visemeProgress, 1));
95
-
96
- // Smooth the weight transition
97
- const smoothedWeight = lastVisemeRef.current
98
- ? lastVisemeRef.current.weight + (targetWeight - lastVisemeRef.current.weight) * SMOOTHING_FACTOR
99
- : targetWeight;
100
-
101
- const updatedViseme = { ...currentViseme, weight: smoothedWeight };
102
- lastVisemeRef.current = updatedViseme;
103
- return updatedViseme;
104
- }
105
-
106
- // Gradually reduce weight when no viseme is active
107
- if (lastVisemeRef.current) {
108
- const reducedWeight = lastVisemeRef.current.weight * (1 - SMOOTHING_FACTOR);
109
- if (reducedWeight > 0.01) {
110
- lastVisemeRef.current = { ...lastVisemeRef.current, weight: reducedWeight };
111
- return lastVisemeRef.current;
89
+ const updateCurrentViseme = useCallback(
90
+ (currentTime: number): Viseme | null => {
91
+ if (!isProcessing || startTimeRef.current === null) {
92
+ console.log('StartTimeRef not set');
93
+ return null;
112
94
  }
113
- }
114
95
 
115
- lastVisemeRef.current = null;
116
- return null;
117
- }, [isProcessing]);
96
+ const elapsedTime = currentTime - startTimeRef.current + PRELOAD_TIME;
97
+
98
+ // Remove expired visemes
99
+ visemeQueueRef.current = visemeQueueRef.current.filter(
100
+ v => v.endTime > elapsedTime
101
+ );
102
+
103
+ const currentViseme = visemeQueueRef.current.find(
104
+ v =>
105
+ v.startTime <= elapsedTime && v.endTime > elapsedTime - VISEME_OVERLAP
106
+ );
107
+
108
+ if (currentViseme) {
109
+ console.log('CurrentViseme Found!');
110
+ const visemeProgress =
111
+ (elapsedTime - currentViseme.startTime) /
112
+ (currentViseme.endTime - currentViseme.startTime);
113
+ const targetWeight = Math.sin(Math.PI * Math.min(visemeProgress, 1));
114
+
115
+ // Smooth the weight transition
116
+ const smoothedWeight = lastVisemeRef.current
117
+ ? lastVisemeRef.current.weight +
118
+ (targetWeight - lastVisemeRef.current.weight) * SMOOTHING_FACTOR
119
+ : targetWeight;
120
+
121
+ const updatedViseme = { ...currentViseme, weight: smoothedWeight };
122
+ lastVisemeRef.current = updatedViseme;
123
+ return updatedViseme;
124
+ }
125
+
126
+ // Gradually reduce weight when no viseme is active
127
+ if (lastVisemeRef.current) {
128
+ const reducedWeight =
129
+ lastVisemeRef.current.weight * (1 - SMOOTHING_FACTOR);
130
+ if (reducedWeight > 0.01) {
131
+ lastVisemeRef.current = {
132
+ ...lastVisemeRef.current,
133
+ weight: reducedWeight,
134
+ };
135
+ return lastVisemeRef.current;
136
+ }
137
+ }
138
+
139
+ lastVisemeRef.current = null;
140
+ return null;
141
+ },
142
+ [isProcessing]
143
+ );
118
144
 
119
145
  const startProcessing = useCallback(() => {
120
- if (isProcessing) return;
146
+ // if (isProcessing) return;
121
147
  startTimeRef.current = performance.now() / 1000;
122
148
  setIsProcessing(true);
123
149
  }, [isProcessing]);
@@ -133,11 +159,19 @@ export const VisemeProvider: React.FC<{ children: React.ReactNode }> = ({ childr
133
159
  lastVisemeRef.current = null;
134
160
  }, []);
135
161
 
162
+ const resetAndStartProcessing = useCallback(() => {
163
+ stopProcessing();
164
+ resetVisemeQueue();
165
+ startTimeRef.current = performance.now() / 1000;
166
+ setIsProcessing(true);
167
+ }, [stopProcessing, resetVisemeQueue]);
168
+
136
169
  const contextValue = {
137
170
  addViseme,
138
171
  updateCurrentViseme,
139
172
  startProcessing,
140
173
  stopProcessing,
174
+ resetAndStartProcessing,
141
175
  resetVisemeQueue,
142
176
  isProcessing,
143
177
  };
@@ -155,4 +189,4 @@ export const useViseme = (): VisemeContextType => {
155
189
  throw new Error('useViseme must be used within a VisemeProvider');
156
190
  }
157
191
  return context;
158
- };
192
+ };