@memori.ai/memori-react 2.7.2 → 2.8.1

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 (46) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +49 -28
  3. package/dist/components/Chat/Chat.d.ts +2 -0
  4. package/dist/components/Chat/Chat.js +2 -2
  5. package/dist/components/Chat/Chat.js.map +1 -1
  6. package/dist/components/MediaWidget/MediaItemWidget.d.ts +3 -1
  7. package/dist/components/MediaWidget/MediaItemWidget.js +8 -4
  8. package/dist/components/MediaWidget/MediaItemWidget.js.map +1 -1
  9. package/dist/components/MediaWidget/MediaItemWidget.test.js +4 -0
  10. package/dist/components/MediaWidget/MediaItemWidget.test.js.map +1 -1
  11. package/dist/components/MediaWidget/MediaWidget.d.ts +2 -0
  12. package/dist/components/MediaWidget/MediaWidget.js +2 -2
  13. package/dist/components/MediaWidget/MediaWidget.js.map +1 -1
  14. package/dist/components/MemoriWidget/MemoriWidget.d.ts +2 -1
  15. package/dist/components/MemoriWidget/MemoriWidget.js +20 -5
  16. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +2 -2
  19. package/dist/index.js.map +1 -1
  20. package/esm/components/Chat/Chat.d.ts +2 -0
  21. package/esm/components/Chat/Chat.js +2 -2
  22. package/esm/components/Chat/Chat.js.map +1 -1
  23. package/esm/components/MediaWidget/MediaItemWidget.d.ts +3 -1
  24. package/esm/components/MediaWidget/MediaItemWidget.js +8 -4
  25. package/esm/components/MediaWidget/MediaItemWidget.js.map +1 -1
  26. package/esm/components/MediaWidget/MediaItemWidget.test.js +4 -0
  27. package/esm/components/MediaWidget/MediaItemWidget.test.js.map +1 -1
  28. package/esm/components/MediaWidget/MediaWidget.d.ts +2 -0
  29. package/esm/components/MediaWidget/MediaWidget.js +2 -2
  30. package/esm/components/MediaWidget/MediaWidget.js.map +1 -1
  31. package/esm/components/MemoriWidget/MemoriWidget.d.ts +2 -1
  32. package/esm/components/MemoriWidget/MemoriWidget.js +20 -5
  33. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  34. package/esm/index.d.ts +1 -0
  35. package/esm/index.js +2 -2
  36. package/esm/index.js.map +1 -1
  37. package/package.json +1 -1
  38. package/src/components/Chat/Chat.tsx +6 -1
  39. package/src/components/MediaWidget/MediaItemWidget.stories.tsx +25 -0
  40. package/src/components/MediaWidget/MediaItemWidget.test.tsx +11 -0
  41. package/src/components/MediaWidget/MediaItemWidget.tsx +12 -0
  42. package/src/components/MediaWidget/MediaWidget.tsx +4 -1
  43. package/src/components/MediaWidget/__snapshots__/MediaItemWidget.test.tsx.snap +12 -0
  44. package/src/components/MemoriWidget/MemoriWidget.stories.tsx +21 -0
  45. package/src/components/MemoriWidget/MemoriWidget.tsx +173 -150
  46. package/src/index.tsx +3 -0
@@ -5,7 +5,7 @@ import {
5
5
  import React, { useEffect, useState } from 'react';
6
6
  import Button from '../ui/Button';
7
7
  import LinkItemWidget from './LinkItemWidget';
8
- import MediaItemWidget from './MediaItemWidget';
8
+ import MediaItemWidget, { Props as MediaItemProps } from './MediaItemWidget';
9
9
  import { Transition } from '@headlessui/react';
10
10
  import cx from 'classnames';
11
11
  import { useTranslation } from 'react-i18next';
@@ -19,6 +19,7 @@ export interface Props {
19
19
  baseUrl?: string;
20
20
  apiUrl?: string;
21
21
  translateTo?: string;
22
+ customMediaRenderer?: MediaItemProps['customMediaRenderer'];
22
23
  }
23
24
 
24
25
  const MediaWidget: React.FC<Props> = ({
@@ -30,6 +31,7 @@ const MediaWidget: React.FC<Props> = ({
30
31
  baseUrl,
31
32
  apiUrl,
32
33
  translateTo,
34
+ customMediaRenderer,
33
35
  }: Props) => {
34
36
  const { t } = useTranslation();
35
37
  const [showHints, setShowHints] = useState(true);
@@ -48,6 +50,7 @@ const MediaWidget: React.FC<Props> = ({
48
50
  translateTo={translateTo}
49
51
  baseURL={baseUrl}
50
52
  apiURL={apiUrl}
53
+ customMediaRenderer={customMediaRenderer}
51
54
  />
52
55
  )}
53
56
  {links?.length > 0 && <LinkItemWidget items={links} baseUrl={baseUrl} />}
@@ -151,6 +151,18 @@ exports[`renders MediaItemWidget unchanged with css snippet to show 1`] = `
151
151
  </div>
152
152
  `;
153
153
 
154
+ exports[`renders MediaItemWidget unchanged with custom media renderer 1`] = `
155
+ <div>
156
+ <div
157
+ class="memori-media-items"
158
+ >
159
+ <div
160
+ class="memori-media-items--grid"
161
+ />
162
+ </div>
163
+ </div>
164
+ `;
165
+
154
166
  exports[`renders MediaItemWidget unchanged with img 1`] = `
155
167
  <div>
156
168
  <div
@@ -127,3 +127,24 @@ WithAzureSpeechKey.args = {
127
127
  tenant,
128
128
  AZURE_COGNITIVE_SERVICES_TTS_KEY: 'provide your key here',
129
129
  };
130
+
131
+ export const WithCustomMediaRenderer = Template.bind({});
132
+ WithCustomMediaRenderer.args = {
133
+ memori,
134
+ tenant,
135
+ customMediaRenderer: (mimeType: string) => (
136
+ <div
137
+ style={{
138
+ width: '100%',
139
+ height: '100%',
140
+ backgroundColor: 'black',
141
+ color: 'white',
142
+ display: 'flex',
143
+ justifyContent: 'center',
144
+ alignItems: 'center',
145
+ }}
146
+ >
147
+ {mimeType}
148
+ </div>
149
+ ),
150
+ };
@@ -148,6 +148,7 @@ let audioContext: IAudioContext;
148
148
 
149
149
  let memoriPassword: string | undefined;
150
150
  let speakerMuted: boolean = false;
151
+ let speakLanguage: string | undefined;
151
152
 
152
153
  export interface LayoutProps {
153
154
  Header?: typeof Header;
@@ -203,6 +204,7 @@ export interface Props {
203
204
  AZURE_COGNITIVE_SERVICES_TTS_KEY?: string;
204
205
  onStateChange?: (state?: DialogState) => void;
205
206
  additionalInfo?: OpenSession['additionalInfo'] & { [key: string]: string };
207
+ customMediaRenderer?: ChatProps['customMediaRenderer'];
206
208
  }
207
209
 
208
210
  const MemoriWidget = ({
@@ -235,6 +237,7 @@ const MemoriWidget = ({
235
237
  AZURE_COGNITIVE_SERVICES_TTS_KEY,
236
238
  onStateChange,
237
239
  additionalInfo,
240
+ customMediaRenderer,
238
241
  }: Props) => {
239
242
  const { t, i18n } = useTranslation();
240
243
 
@@ -271,7 +274,7 @@ const MemoriWidget = ({
271
274
  multilingual !== undefined
272
275
  ? multilingual
273
276
  : !!integrationConfig?.multilanguage;
274
- const [userLang, setUserLang] = useState(
277
+ const [userLang, _setUserLang] = useState(
275
278
  memoriLang ??
276
279
  integrationConfig?.lang ??
277
280
  memori?.culture?.split('-')?.[0] ??
@@ -280,6 +283,10 @@ const MemoriWidget = ({
280
283
  i18n.language ??
281
284
  'IT'
282
285
  );
286
+ const setUserLang: typeof _setUserLang = lang => {
287
+ _setUserLang(lang);
288
+ speakLanguage = lang;
289
+ };
283
290
 
284
291
  const [loading, setLoading] = useState(false);
285
292
  const [memoriTyping, setMemoriTyping] = useState(false);
@@ -1141,6 +1148,13 @@ const MemoriWidget = ({
1141
1148
  : 'ru-RU-SvetlanaNeural'
1142
1149
  }`;
1143
1150
  break;
1151
+ case 'PL':
1152
+ voice = `${
1153
+ memori.voiceType === 'MALE'
1154
+ ? 'pl-PL-MarekNeural'
1155
+ : 'pl-PL-AgnieszkaNeural'
1156
+ }`;
1157
+ break;
1144
1158
  default:
1145
1159
  voice = `${
1146
1160
  memori.voiceType === 'MALE'
@@ -1187,6 +1201,9 @@ const MemoriWidget = ({
1187
1201
  case 'RU':
1188
1202
  voice = 'ru-RU';
1189
1203
  break;
1204
+ case 'PL':
1205
+ voice = 'pl-PL';
1206
+ break;
1190
1207
  default:
1191
1208
  voice = 'it-IT';
1192
1209
  break;
@@ -1266,177 +1283,182 @@ const MemoriWidget = ({
1266
1283
  // .replace(/qfe/gi, `<sub alias="Quota Filo Erba">QFE</sub>`)
1267
1284
  };
1268
1285
 
1269
- const speak = (text: string): void => {
1270
- if (!AZURE_COGNITIVE_SERVICES_TTS_KEY) return;
1271
- stopListening();
1272
- // stopAudio();
1286
+ const speak = useCallback(
1287
+ (text: string): void => {
1288
+ if (!AZURE_COGNITIVE_SERVICES_TTS_KEY) return;
1289
+ stopListening();
1290
+ // stopAudio();
1273
1291
 
1274
- if (preview) return;
1292
+ if (preview) return;
1275
1293
 
1276
- if (muteSpeaker || speakerMuted) {
1277
- // trigger start continuous listening if set, see MemoriChat
1278
- if (continuousSpeech) {
1279
- setListeningTimeout();
1294
+ if (muteSpeaker || speakerMuted) {
1295
+ // trigger start continuous listening if set, see MemoriChat
1296
+ if (continuousSpeech) {
1297
+ setListeningTimeout();
1298
+ }
1299
+ return;
1280
1300
  }
1281
- return;
1282
- }
1283
1301
 
1284
- if (audioDestination) audioDestination.pause();
1285
-
1286
- // if (audioContext?.state === 'running') {
1287
- // // audioContext.suspend();
1288
- // // }
1289
- // // if (audioContext) {
1290
- // audioContext.close().then(() => {
1291
- // audioContext = new AudioContext();
1292
- // let buffer = audioContext.createBuffer(1, 10000, 22050);
1293
- // let source = audioContext.createBufferSource();
1294
- // source.buffer = buffer;
1295
- // source.connect(audioContext.destination);
1296
- // });
1297
- // }
1298
-
1299
- let isSafari =
1300
- window.navigator.userAgent.includes('Safari') &&
1301
- !window.navigator.userAgent.includes('Chrome');
1302
- let isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
1303
- if ((audioContext.state as string) === 'interrupted') {
1304
- audioContext.resume().then(() => speak(text));
1305
- return;
1306
- }
1307
- if (isIOS && isSafari) {
1308
- audioContext.suspend();
1309
-
1310
- if (isPlayingAudio) {
1311
- try {
1312
- if (speechSynthesizer) {
1313
- speechSynthesizer.close();
1314
- speechSynthesizer = null;
1315
- }
1316
- if (audioDestination) {
1317
- audioDestination.pause();
1318
- audioDestination.close();
1319
- }
1320
- if (audioContext) {
1321
- // audioContext.close().then(() => {
1322
- // audioContext = new AudioContext();
1323
- // let buffer = audioContext.createBuffer(1, 10000, 22050);
1324
- // let source = audioContext.createBufferSource();
1325
- // source.buffer = buffer;
1326
- // source.connect(audioContext.destination);
1327
- // });
1328
- audioContext.destination.disconnect();
1302
+ if (audioDestination) audioDestination.pause();
1303
+
1304
+ // if (audioContext?.state === 'running') {
1305
+ // // audioContext.suspend();
1306
+ // // }
1307
+ // // if (audioContext) {
1308
+ // audioContext.close().then(() => {
1309
+ // audioContext = new AudioContext();
1310
+ // let buffer = audioContext.createBuffer(1, 10000, 22050);
1311
+ // let source = audioContext.createBufferSource();
1312
+ // source.buffer = buffer;
1313
+ // source.connect(audioContext.destination);
1314
+ // });
1315
+ // }
1316
+
1317
+ let isSafari =
1318
+ window.navigator.userAgent.includes('Safari') &&
1319
+ !window.navigator.userAgent.includes('Chrome');
1320
+ let isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
1321
+ if ((audioContext.state as string) === 'interrupted') {
1322
+ audioContext.resume().then(() => speak(text));
1323
+ return;
1324
+ }
1325
+ if (isIOS && isSafari) {
1326
+ audioContext.suspend();
1327
+
1328
+ if (isPlayingAudio) {
1329
+ try {
1330
+ if (speechSynthesizer) {
1331
+ speechSynthesizer.close();
1332
+ speechSynthesizer = null;
1333
+ }
1334
+ if (audioDestination) {
1335
+ audioDestination.pause();
1336
+ audioDestination.close();
1337
+ }
1338
+ if (audioContext) {
1339
+ // audioContext.close().then(() => {
1340
+ // audioContext = new AudioContext();
1341
+ // let buffer = audioContext.createBuffer(1, 10000, 22050);
1342
+ // let source = audioContext.createBufferSource();
1343
+ // source.buffer = buffer;
1344
+ // source.connect(audioContext.destination);
1345
+ // });
1346
+ audioContext.destination.disconnect();
1347
+ }
1348
+ } catch (e) {
1349
+ console.error('stopAudio error: ', e);
1329
1350
  }
1330
- } catch (e) {
1331
- console.error('stopAudio error: ', e);
1332
1351
  }
1333
1352
  }
1334
- }
1335
- if (audioContext.state === 'closed') {
1336
- audioContext = new AudioContext();
1337
- let buffer = audioContext.createBuffer(1, 10000, 22050);
1338
- let source = audioContext.createBufferSource();
1339
- source.buffer = buffer;
1340
- source.connect(audioContext.destination);
1341
- } else if (audioContext.state === 'suspended') {
1342
- stopAudio();
1353
+ if (audioContext.state === 'closed') {
1354
+ audioContext = new AudioContext();
1355
+ let buffer = audioContext.createBuffer(1, 10000, 22050);
1356
+ let source = audioContext.createBufferSource();
1357
+ source.buffer = buffer;
1358
+ source.connect(audioContext.destination);
1359
+ } else if (audioContext.state === 'suspended') {
1360
+ stopAudio();
1343
1361
 
1344
- audioContext = new AudioContext();
1345
- let buffer = audioContext.createBuffer(1, 10000, 22050);
1346
- let source = audioContext.createBufferSource();
1347
- source.buffer = buffer;
1348
- source.connect(audioContext.destination);
1349
- }
1362
+ audioContext = new AudioContext();
1363
+ let buffer = audioContext.createBuffer(1, 10000, 22050);
1364
+ let source = audioContext.createBufferSource();
1365
+ source.buffer = buffer;
1366
+ source.connect(audioContext.destination);
1367
+ }
1350
1368
 
1351
- if (!speechSynthesizer) {
1352
- audioDestination = new speechSdk.SpeakerAudioDestination();
1353
- let audioConfig =
1354
- speechSdk.AudioConfig.fromSpeakerOutput(audioDestination);
1355
- speechSynthesizer = new speechSdk.SpeechSynthesizer(
1356
- speechConfig,
1357
- audioConfig
1358
- );
1359
- }
1369
+ if (!speechSynthesizer) {
1370
+ audioDestination = new speechSdk.SpeakerAudioDestination();
1371
+ let audioConfig =
1372
+ speechSdk.AudioConfig.fromSpeakerOutput(audioDestination);
1373
+ speechSynthesizer = new speechSdk.SpeechSynthesizer(
1374
+ speechConfig,
1375
+ audioConfig
1376
+ );
1377
+ }
1360
1378
 
1361
- const source = audioContext.createBufferSource();
1362
- source.addEventListener('ended', () => {
1363
- setIsPlayingAudio(false);
1364
- });
1365
- audioDestination.onAudioEnd = () => {
1366
- setIsPlayingAudio(false);
1367
- source.disconnect();
1379
+ const source = audioContext.createBufferSource();
1380
+ source.addEventListener('ended', () => {
1381
+ setIsPlayingAudio(false);
1382
+ });
1383
+ audioDestination.onAudioEnd = () => {
1384
+ setIsPlayingAudio(false);
1385
+ source.disconnect();
1368
1386
 
1369
- // trigger start continuous listening if set
1370
- // document.dispatchEvent(new Event('endSpeakStartListen'));
1371
- onEndSpeakStartListen();
1372
- };
1387
+ // trigger start continuous listening if set
1388
+ // document.dispatchEvent(new Event('endSpeakStartListen'));
1389
+ onEndSpeakStartListen();
1390
+ };
1373
1391
 
1374
- speechSynthesizer.speakSsmlAsync(
1375
- `<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(
1376
- userLang
1377
- )}"><voice name="${getTTSVoice(userLang)}"><s>${replaceTextWithPhonemes(
1378
- escapeHTML(text),
1379
- userLang.toLowerCase()
1380
- )}</s></voice></speak>`,
1381
- result => {
1382
- if (result) {
1383
- setIsPlayingAudio(true);
1392
+ const lang = speakLanguage || userLang;
1384
1393
 
1385
- try {
1386
- // if (audioContext.destination.context.state === 'running') {
1387
- // audioContext.destination.disconnect();
1388
- // }
1389
- audioContext.decodeAudioData(result.audioData, function (buffer) {
1390
- source.buffer = buffer;
1391
- source.connect(audioContext.destination);
1392
-
1393
- if (history.length < 1 || (isSafari && isIOS)) {
1394
- source.start(0);
1395
- }
1396
- });
1394
+ speechSynthesizer.speakSsmlAsync(
1395
+ `<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(
1396
+ lang
1397
+ )}"><voice name="${getTTSVoice(lang)}"><s>${replaceTextWithPhonemes(
1398
+ escapeHTML(text),
1399
+ lang.toLowerCase()
1400
+ )}</s></voice></speak>`,
1401
+ result => {
1402
+ if (result) {
1403
+ setIsPlayingAudio(true);
1397
1404
 
1398
- audioContext.onstatechange = () => {
1399
- if (
1400
- audioContext.state === 'suspended' ||
1401
- audioContext.state === 'closed'
1402
- ) {
1403
- source.disconnect();
1404
- setIsPlayingAudio(false);
1405
- } else if ((audioContext.state as string) === 'interrupted') {
1406
- audioContext.resume();
1407
- }
1408
- };
1405
+ try {
1406
+ // if (audioContext.destination.context.state === 'running') {
1407
+ // audioContext.destination.disconnect();
1408
+ // }
1409
+ audioContext.decodeAudioData(result.audioData, function (buffer) {
1410
+ source.buffer = buffer;
1411
+ source.connect(audioContext.destination);
1412
+
1413
+ if (history.length < 1 || (isSafari && isIOS)) {
1414
+ source.start(0);
1415
+ }
1416
+ });
1409
1417
 
1410
- audioContext.resume();
1418
+ audioContext.onstatechange = () => {
1419
+ if (
1420
+ audioContext.state === 'suspended' ||
1421
+ audioContext.state === 'closed'
1422
+ ) {
1423
+ source.disconnect();
1424
+ setIsPlayingAudio(false);
1425
+ } else if ((audioContext.state as string) === 'interrupted') {
1426
+ audioContext.resume();
1427
+ }
1428
+ };
1411
1429
 
1412
- if (speechSynthesizer) {
1413
- speechSynthesizer.close();
1414
- speechSynthesizer = null;
1415
- }
1416
- } catch (e) {
1417
- console.error('speak error: ', e);
1418
- window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
1419
- setIsPlayingAudio(false);
1430
+ audioContext.resume();
1420
1431
 
1421
- if (speechSynthesizer) {
1422
- speechSynthesizer.close();
1423
- speechSynthesizer = null;
1432
+ if (speechSynthesizer) {
1433
+ speechSynthesizer.close();
1434
+ speechSynthesizer = null;
1435
+ }
1436
+ } catch (e) {
1437
+ console.error('speak error: ', e);
1438
+ window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
1439
+ setIsPlayingAudio(false);
1440
+
1441
+ if (speechSynthesizer) {
1442
+ speechSynthesizer.close();
1443
+ speechSynthesizer = null;
1444
+ }
1424
1445
  }
1446
+ } else {
1447
+ audioContext.resume();
1448
+ setIsPlayingAudio(false);
1425
1449
  }
1426
- } else {
1427
- audioContext.resume();
1450
+ },
1451
+ error => {
1452
+ console.error('speak:', error);
1453
+ window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
1428
1454
  setIsPlayingAudio(false);
1429
1455
  }
1430
- },
1431
- error => {
1432
- console.error('speak:', error);
1433
- window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
1434
- setIsPlayingAudio(false);
1435
- }
1436
- );
1456
+ );
1437
1457
 
1438
- setMemoriTyping(false);
1439
- };
1458
+ setMemoriTyping(false);
1459
+ },
1460
+ [userLang, speakerMuted]
1461
+ );
1440
1462
  const stopAudio = () => {
1441
1463
  setIsPlayingAudio(false);
1442
1464
  try {
@@ -2312,6 +2334,7 @@ const MemoriWidget = ({
2312
2334
  resetTranscript,
2313
2335
  listening,
2314
2336
  isPlayingAudio,
2337
+ customMediaRenderer,
2315
2338
  };
2316
2339
 
2317
2340
  const integrationBackground =
package/src/index.tsx CHANGED
@@ -40,6 +40,7 @@ export interface Props {
40
40
  AZURE_COGNITIVE_SERVICES_TTS_KEY?: string;
41
41
  onStateChange?: (state?: DialogState) => void;
42
42
  additionalInfo?: WidgetProps['additionalInfo'];
43
+ customMediaRenderer?: WidgetProps['customMediaRenderer'];
43
44
  }
44
45
 
45
46
  const getPreferredLanguages = () => {
@@ -87,6 +88,7 @@ const Memori: React.FC<Props> = ({
87
88
  AZURE_COGNITIVE_SERVICES_TTS_KEY,
88
89
  onStateChange,
89
90
  additionalInfo,
91
+ customMediaRenderer,
90
92
  }) => {
91
93
  const [memori, setMemori] = useState<IMemori>();
92
94
  const [speechKey, setSpeechKey] = useState<string | undefined>(
@@ -213,6 +215,7 @@ const Memori: React.FC<Props> = ({
213
215
  }
214
216
  onStateChange={onStateChange}
215
217
  additionalInfo={additionalInfo}
218
+ customMediaRenderer={customMediaRenderer}
216
219
  {...(tag && pin ? { personification: { tag, pin } } : {})}
217
220
  />
218
221
  ) : (