@memori.ai/memori-react 8.45.2 → 8.45.4
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.
- package/CHANGELOG.md +20 -0
- package/dist/components/Chat/Chat.js +4 -1
- package/dist/components/Chat/Chat.js.map +1 -1
- package/dist/components/ChatHistoryDrawer/ChatResumeDrawer.d.ts +39 -0
- package/dist/components/ChatHistoryDrawer/ChatResumeDrawer.js +66 -0
- package/dist/components/ChatHistoryDrawer/ChatResumeDrawer.js.map +1 -0
- package/dist/components/DrawerFooter/DrawerFooter.d.ts +12 -0
- package/dist/components/DrawerFooter/DrawerFooter.js +10 -0
- package/dist/components/DrawerFooter/DrawerFooter.js.map +1 -0
- package/dist/components/IconButton/IconButton.d.ts +9 -0
- package/dist/components/IconButton/IconButton.js +36 -0
- package/dist/components/IconButton/IconButton.js.map +1 -0
- package/dist/components/MemoriWidget/MemoriWidget.d.ts +1 -1
- package/dist/components/MemoriWidget/MemoriWidget.js +2 -1
- package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/dist/components/MobileSessionPanel/MobileSessionPanel.d.ts +65 -0
- package/dist/components/MobileSessionPanel/MobileSessionPanel.js +294 -0
- package/dist/components/MobileSessionPanel/MobileSessionPanel.js.map +1 -0
- package/dist/components/PositionPopover/PositionPopover.d.ts +22 -0
- package/dist/components/PositionPopover/PositionPopover.js +208 -0
- package/dist/components/PositionPopover/PositionPopover.js.map +1 -0
- package/dist/components/layouts/WebsiteAssistant/WebsiteAssistant.d.ts +4 -0
- package/dist/components/layouts/WebsiteAssistant/WebsiteAssistant.js +64 -0
- package/dist/components/layouts/WebsiteAssistant/WebsiteAssistant.js.map +1 -0
- package/dist/components/layouts/chat.css +27 -4
- package/dist/context/widgetSurfaceContext.d.ts +3 -0
- package/dist/context/widgetSurfaceContext.js +11 -0
- package/dist/context/widgetSurfaceContext.js.map +1 -0
- package/dist/helpers/positionPopover.d.ts +2 -0
- package/dist/helpers/positionPopover.js +16 -0
- package/dist/helpers/positionPopover.js.map +1 -0
- package/dist/helpers/tts/muteSpeaker.d.ts +13 -0
- package/dist/helpers/tts/muteSpeaker.js +27 -0
- package/dist/helpers/tts/muteSpeaker.js.map +1 -0
- package/dist/helpers/usePressTooltip.d.ts +13 -0
- package/dist/helpers/usePressTooltip.js +24 -0
- package/dist/helpers/usePressTooltip.js.map +1 -0
- package/dist/icons/FacebookIcon.d.ts +3 -0
- package/dist/icons/FacebookIcon.js +6 -0
- package/dist/icons/FacebookIcon.js.map +1 -0
- package/dist/icons/LinkedinIcon.d.ts +3 -0
- package/dist/icons/LinkedinIcon.js +6 -0
- package/dist/icons/LinkedinIcon.js.map +1 -0
- package/dist/icons/MemoriIcon.d.ts +3 -0
- package/dist/icons/MemoriIcon.js +8 -0
- package/dist/icons/MemoriIcon.js.map +1 -0
- package/dist/icons/TelegramIcon.d.ts +3 -0
- package/dist/icons/TelegramIcon.js +6 -0
- package/dist/icons/TelegramIcon.js.map +1 -0
- package/dist/icons/TwitterIcon.d.ts +3 -0
- package/dist/icons/TwitterIcon.js +6 -0
- package/dist/icons/TwitterIcon.js.map +1 -0
- package/dist/icons/WhatsappIcon.d.ts +3 -0
- package/dist/icons/WhatsappIcon.js +6 -0
- package/dist/icons/WhatsappIcon.js.map +1 -0
- package/dist/icons/artifacts/DocumentIcon.d.ts +3 -0
- package/dist/icons/artifacts/DocumentIcon.js +6 -0
- package/dist/icons/artifacts/DocumentIcon.js.map +1 -0
- package/dist/icons/artifacts/HtmlIcon.d.ts +3 -0
- package/dist/icons/artifacts/HtmlIcon.js +6 -0
- package/dist/icons/artifacts/HtmlIcon.js.map +1 -0
- package/dist/icons/artifacts/JavascriptIcon.d.ts +3 -0
- package/dist/icons/artifacts/JavascriptIcon.js +6 -0
- package/dist/icons/artifacts/JavascriptIcon.js.map +1 -0
- package/dist/icons/artifacts/MarkdownIcon.d.ts +3 -0
- package/dist/icons/artifacts/MarkdownIcon.js +6 -0
- package/dist/icons/artifacts/MarkdownIcon.js.map +1 -0
- package/dist/icons/artifacts/PythonIcon.d.ts +3 -0
- package/dist/icons/artifacts/PythonIcon.js +6 -0
- package/dist/icons/artifacts/PythonIcon.js.map +1 -0
- package/dist/icons/artifacts/SvgIcon.d.ts +3 -0
- package/dist/icons/artifacts/SvgIcon.js +6 -0
- package/dist/icons/artifacts/SvgIcon.js.map +1 -0
- package/dist/icons/artifacts/XmlIcon.d.ts +3 -0
- package/dist/icons/artifacts/XmlIcon.js +6 -0
- package/dist/icons/artifacts/XmlIcon.js.map +1 -0
- package/dist/icons/artifacts/cssIcon.d.ts +3 -0
- package/dist/icons/artifacts/cssIcon.js +6 -0
- package/dist/icons/artifacts/cssIcon.js.map +1 -0
- package/dist/styles.css +1 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/esm/components/Chat/Chat.js +4 -1
- package/esm/components/Chat/Chat.js.map +1 -1
- package/esm/components/MemoriWidget/MemoriWidget.d.ts +1 -1
- package/esm/components/MemoriWidget/MemoriWidget.js +2 -1
- package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/esm/components/layouts/chat.css +27 -4
- package/esm/styles.css +1 -2
- package/esm/version.d.ts +1 -1
- package/esm/version.js +1 -1
- package/package.json +6 -7
- package/dist/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.d.ts +0 -3
- package/dist/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.js +0 -64
- package/dist/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.js.map +0 -1
- package/esm/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.d.ts +0 -3
- package/esm/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.js +0 -62
- package/esm/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.js.map +0 -1
- package/src/I18nWrapper.tsx +0 -13
- package/src/__snapshots__/index.test.tsx.snap +0 -96
- package/src/components/AgeVerificationModal/AgeVerificationModal.css +0 -95
- package/src/components/AgeVerificationModal/AgeVerificationModal.stories.tsx +0 -46
- package/src/components/AgeVerificationModal/AgeVerificationModal.test.tsx +0 -33
- package/src/components/AgeVerificationModal/AgeVerificationModal.tsx +0 -99
- package/src/components/AgeVerificationModal/__snapshots__/AgeVerificationModal.test.tsx.snap +0 -19
- package/src/components/Auth/Auth.css +0 -57
- package/src/components/Auth/Auth.stories.tsx +0 -90
- package/src/components/Auth/Auth.test.tsx +0 -63
- package/src/components/Auth/Auth.tsx +0 -201
- package/src/components/Auth/__snapshots__/Auth.test.tsx.snap +0 -143
- package/src/components/Avatar/Avatar.css +0 -151
- package/src/components/Avatar/Avatar.stories.tsx +0 -160
- package/src/components/Avatar/Avatar.test.tsx +0 -158
- package/src/components/Avatar/Avatar.tsx +0 -245
- package/src/components/Avatar/AvatarView/AvatarComponent/Shadow/DynamicShadow.tsx +0 -103
- package/src/components/Avatar/AvatarView/AvatarComponent/avatarComponent.tsx +0 -105
- package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.tsx +0 -338
- package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.ts +0 -44
- package/src/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.ts +0 -1091
- package/src/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarPositionController.ts +0 -83
- package/src/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.ts +0 -301
- package/src/components/Avatar/AvatarView/AvatarComponent/components/controls.tsx +0 -100
- package/src/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.tsx +0 -152
- package/src/components/Avatar/AvatarView/AvatarComponent/components/loader.tsx +0 -31
- package/src/components/Avatar/AvatarView/AvatarComponent/constants.ts +0 -141
- package/src/components/Avatar/AvatarView/AvatarComponent/lights/Lights.tsx +0 -145
- package/src/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.css +0 -111
- package/src/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.tsx +0 -201
- package/src/components/Avatar/AvatarView/AvatarView.stories.tsx +0 -254
- package/src/components/Avatar/AvatarView/index.tsx +0 -194
- package/src/components/Avatar/AvatarView/utils/hideHands.ts +0 -11
- package/src/components/Avatar/AvatarView/utils/useEyeBlink.ts +0 -100
- package/src/components/Avatar/AvatarView/utils/useHeadMovement.ts +0 -72
- package/src/components/Avatar/AvatarView/utils/useLoadingMorphAnim.ts +0 -38
- package/src/components/Avatar/AvatarView/utils/useMouthAnimation.ts +0 -93
- package/src/components/Avatar/AvatarView/utils/useSmile.ts +0 -39
- package/src/components/Avatar/AvatarView/utils/utils.ts +0 -55
- package/src/components/Avatar/__snapshots__/Avatar.test.tsx.snap +0 -246
- package/src/components/Blob/Blob.css +0 -286
- package/src/components/Blob/Blob.stories.tsx +0 -53
- package/src/components/Blob/Blob.test.tsx +0 -18
- package/src/components/Blob/Blob.tsx +0 -26
- package/src/components/Blob/__snapshots__/Blob.test.tsx.snap +0 -80
- package/src/components/BlockedMemoriBadge/BlockedMemoriBadge.css +0 -40
- package/src/components/BlockedMemoriBadge/BlockedMemoriBadge.stories.tsx +0 -58
- package/src/components/BlockedMemoriBadge/BlockedMemoriBadge.test.tsx +0 -53
- package/src/components/BlockedMemoriBadge/BlockedMemoriBadge.tsx +0 -74
- package/src/components/BlockedMemoriBadge/__snapshots__/BlockedMemoriBadge.test.tsx.snap +0 -181
- package/src/components/ChangeMode/ChangeMode.css +0 -22
- package/src/components/ChangeMode/ChangeMode.stories.tsx +0 -50
- package/src/components/ChangeMode/ChangeMode.test.tsx +0 -24
- package/src/components/ChangeMode/ChangeMode.tsx +0 -51
- package/src/components/ChangeMode/__snapshots__/ChangeMode.test.tsx.snap +0 -107
- package/src/components/Chat/Chat.css +0 -285
- package/src/components/Chat/Chat.stories.tsx +0 -1684
- package/src/components/Chat/Chat.test.tsx +0 -372
- package/src/components/Chat/Chat.tsx +0 -778
- package/src/components/Chat/__snapshots__/Chat.test.tsx.snap +0 -6816
- package/src/components/ChatBubble/ChatBubble.css +0 -430
- package/src/components/ChatBubble/ChatBubble.stories.tsx +0 -1192
- package/src/components/ChatBubble/ChatBubble.test.tsx +0 -481
- package/src/components/ChatBubble/ChatBubble.tsx +0 -776
- package/src/components/ChatBubble/__snapshots__/ChatBubble.test.tsx.snap +0 -8734
- package/src/components/ChatHistoryDrawer/ChatHistory.css +0 -587
- package/src/components/ChatHistoryDrawer/ChatHistory.stories.tsx +0 -410
- package/src/components/ChatHistoryDrawer/ChatHistory.test.tsx +0 -107
- package/src/components/ChatHistoryDrawer/ChatHistory.tsx +0 -1298
- package/src/components/ChatHistoryDrawer/__snapshots__/ChatHistory.test.tsx.snap +0 -27
- package/src/components/ChatInputs/ChatInputs.css +0 -340
- package/src/components/ChatInputs/ChatInputs.stories.tsx +0 -251
- package/src/components/ChatInputs/ChatInputs.tsx +0 -487
- package/src/components/ChatTextArea/ChatTextArea.css +0 -185
- package/src/components/ChatTextArea/ChatTextArea.stories.tsx +0 -55
- package/src/components/ChatTextArea/ChatTextArea.test.tsx +0 -28
- package/src/components/ChatTextArea/ChatTextArea.tsx +0 -171
- package/src/components/ChatTextArea/__snapshots__/ChatTextArea.test.tsx.snap +0 -68
- package/src/components/CompletionProviderStatus/CompletionProviderStatus.css +0 -36
- package/src/components/CompletionProviderStatus/CompletionProviderStatus.stories.tsx +0 -296
- package/src/components/CompletionProviderStatus/CompletionProviderStatus.test.tsx +0 -31
- package/src/components/CompletionProviderStatus/CompletionProviderStatus.tsx +0 -232
- package/src/components/CompletionProviderStatus/__snapshots__/CompletionProviderStatus.test.tsx.snap +0 -96
- package/src/components/ContentPreviewModal/ContentPreviewModal.css +0 -128
- package/src/components/ContentPreviewModal/ContentPreviewModal.tsx +0 -67
- package/src/components/ContentPreviewModal/index.ts +0 -2
- package/src/components/CustomGLBModelViewer/ModelViewer.css +0 -11
- package/src/components/CustomGLBModelViewer/ModelViewer.stories.tsx +0 -58
- package/src/components/CustomGLBModelViewer/ModelViewer.test.tsx +0 -13
- package/src/components/CustomGLBModelViewer/ModelViewer.tsx +0 -37
- package/src/components/CustomGLBModelViewer/__snapshots__/ModelViewer.test.tsx.snap +0 -20
- package/src/components/DateSelector/DateSelector.css +0 -158
- package/src/components/DateSelector/DateSelector.stories.tsx +0 -47
- package/src/components/DateSelector/DateSelector.test.tsx +0 -149
- package/src/components/DateSelector/DateSelector.tsx +0 -251
- package/src/components/DateSelector/__snapshots__/DateSelector.test.tsx.snap +0 -1954
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +0 -54
- package/src/components/ExpertsDrawer/ExpertsDrawer.css +0 -67
- package/src/components/ExpertsDrawer/ExpertsDrawer.stories.tsx +0 -54
- package/src/components/ExpertsDrawer/ExpertsDrawer.test.tsx +0 -53
- package/src/components/ExpertsDrawer/ExpertsDrawer.tsx +0 -69
- package/src/components/ExpertsDrawer/__snapshots__/ExpertsDrawer.test.tsx.snap +0 -11
- package/src/components/ExportHistoryButton/ExportHistoryButton.css +0 -22
- package/src/components/FeedbackButtons/FeedbackButtons.css +0 -107
- package/src/components/FeedbackButtons/FeedbackButtons.stories.tsx +0 -52
- package/src/components/FeedbackButtons/FeedbackButtons.test.tsx +0 -11
- package/src/components/FeedbackButtons/FeedbackButtons.tsx +0 -152
- package/src/components/FeedbackButtons/__snapshots__/FeedbackButtons.test.tsx.snap +0 -42
- package/src/components/FilePreview/FilePreview.css +0 -358
- package/src/components/FilePreview/FilePreview.stories.tsx +0 -63
- package/src/components/FilePreview/FilePreview.test.tsx +0 -26
- package/src/components/FilePreview/FilePreview.tsx +0 -277
- package/src/components/FilePreview/__snapshots__/FilePreview.test.tsx.snap +0 -185
- package/src/components/Header/ChatConsumptionDropdown.test.tsx +0 -117
- package/src/components/Header/ChatConsumptionDropdown.tsx +0 -275
- package/src/components/Header/Header.css +0 -282
- package/src/components/Header/Header.stories.tsx +0 -338
- package/src/components/Header/Header.test.tsx +0 -371
- package/src/components/Header/Header.tsx +0 -442
- package/src/components/Header/__snapshots__/Header.test.tsx.snap +0 -964
- package/src/components/KnownFacts/KnownFacts.css +0 -19
- package/src/components/KnownFacts/KnownFacts.stories.tsx +0 -80
- package/src/components/KnownFacts/KnownFacts.test.tsx +0 -57
- package/src/components/KnownFacts/KnownFacts.tsx +0 -376
- package/src/components/KnownFacts/__snapshots__/KnownFacts.test.tsx.snap +0 -19
- package/src/components/LoginDrawer/LoginDrawer.css +0 -1616
- package/src/components/LoginDrawer/LoginDrawer.stories.tsx +0 -102
- package/src/components/LoginDrawer/LoginDrawer.test.tsx +0 -199
- package/src/components/LoginDrawer/LoginDrawer.tsx +0 -600
- package/src/components/LoginDrawer/__snapshots__/LoginDrawer.test.tsx.snap +0 -91
- package/src/components/MediaWidget/DocumentCard.test.tsx +0 -45
- package/src/components/MediaWidget/DocumentCard.tsx +0 -19
- package/src/components/MediaWidget/MediaItemWidget.css +0 -1398
- package/src/components/MediaWidget/MediaItemWidget.stories.tsx +0 -423
- package/src/components/MediaWidget/MediaItemWidget.test.tsx +0 -248
- package/src/components/MediaWidget/MediaItemWidget.tsx +0 -997
- package/src/components/MediaWidget/MediaItemWidget.types.ts +0 -65
- package/src/components/MediaWidget/MediaItemWidget.utils.test.ts +0 -367
- package/src/components/MediaWidget/MediaItemWidget.utils.ts +0 -227
- package/src/components/MediaWidget/MediaPreviewModal.test.tsx +0 -271
- package/src/components/MediaWidget/MediaPreviewModal.tsx +0 -425
- package/src/components/MediaWidget/MediaWidget.css +0 -131
- package/src/components/MediaWidget/MediaWidget.stories.tsx +0 -319
- package/src/components/MediaWidget/MediaWidget.test.tsx +0 -41
- package/src/components/MediaWidget/MediaWidget.tsx +0 -107
- package/src/components/MediaWidget/__snapshots__/DocumentCard.test.tsx.snap +0 -24
- package/src/components/MediaWidget/__snapshots__/MediaItemWidget.test.tsx.snap +0 -791
- package/src/components/MediaWidget/__snapshots__/MediaWidget.test.tsx.snap +0 -154
- package/src/components/MemoriArtifactSystem/ArtifactDrawer.stories.tsx +0 -2299
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +0 -163
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.tsx +0 -250
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/CopyButton.stories.tsx +0 -30
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.tsx +0 -467
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.tsx +0 -89
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/hooks/useCopyArtifact.ts +0 -619
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/index.ts +0 -9
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/styles.css +0 -489
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.tsx +0 -90
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/types.ts +0 -73
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/utils/PDFExporter.ts +0 -454
- package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +0 -1059
- package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.tsx +0 -431
- package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.css +0 -150
- package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.tsx +0 -79
- package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +0 -253
- package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.tsx +0 -561
- package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +0 -355
- package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.tsx +0 -160
- package/src/components/MemoriArtifactSystem/context/ArtifactContext.tsx +0 -167
- package/src/components/MemoriArtifactSystem/types/artifact.types.ts +0 -35
- package/src/components/MemoriArtifactSystem/utils/ArtifactAPI.test.tsx +0 -308
- package/src/components/MemoriArtifactSystem/utils/ArtifactAPI.tsx +0 -379
- package/src/components/MemoriArtifactSystem/utils/applyEdits.test.ts +0 -85
- package/src/components/MemoriArtifactSystem/utils/applyEdits.ts +0 -173
- package/src/components/MemoriWidget/MemoriWidget.css +0 -220
- package/src/components/MemoriWidget/MemoriWidget.stories.tsx +0 -274
- package/src/components/MemoriWidget/MemoriWidget.tsx +0 -3930
- package/src/components/MicrophoneButton/MicrophoneButton.css +0 -142
- package/src/components/MicrophoneButton/MicrophoneButton.stories.tsx +0 -52
- package/src/components/MicrophoneButton/MicrophoneButton.tsx +0 -128
- package/src/components/PositionDrawer/PositionDrawer.stories.tsx +0 -51
- package/src/components/PositionDrawer/PositionDrawer.test.tsx +0 -39
- package/src/components/PositionDrawer/PositionDrawer.tsx +0 -50
- package/src/components/PositionDrawer/__snapshots__/PositionDrawer.test.tsx.snap +0 -17
- package/src/components/PoweredBy/PoweredBy.css +0 -47
- package/src/components/PoweredBy/PoweredBy.stories.tsx +0 -75
- package/src/components/PoweredBy/PoweredBy.test.tsx +0 -15
- package/src/components/PoweredBy/PoweredBy.tsx +0 -44
- package/src/components/PoweredBy/__snapshots__/PoweredBy.test.tsx.snap +0 -55
- package/src/components/SendOnEnterMenu/SendOnEnterMenu.css +0 -47
- package/src/components/SendOnEnterMenu/SendOnEnterMenu.stories.tsx +0 -58
- package/src/components/SendOnEnterMenu/SendOnEnterMenu.test.tsx +0 -17
- package/src/components/SendOnEnterMenu/SendOnEnterMenu.tsx +0 -82
- package/src/components/SendOnEnterMenu/__snapshots__/SendOnEnterMenu.test.tsx.snap +0 -57
- package/src/components/SettingsDrawer/SettingsDrawer.css +0 -24
- package/src/components/SettingsDrawer/SettingsDrawer.stories.tsx +0 -94
- package/src/components/SettingsDrawer/SettingsDrawer.test.tsx +0 -202
- package/src/components/SettingsDrawer/SettingsDrawer.tsx +0 -255
- package/src/components/SettingsDrawer/__snapshots__/SettingsDrawer.test.tsx.snap +0 -59
- package/src/components/ShareButton/ShareButton.css +0 -73
- package/src/components/ShareButton/ShareButton.stories.tsx +0 -98
- package/src/components/ShareButton/ShareButton.test.tsx +0 -53
- package/src/components/ShareButton/ShareButton.tsx +0 -395
- package/src/components/ShareButton/__snapshots__/ShareButton.test.tsx.snap +0 -316
- package/src/components/Snippet/Snippet.css +0 -142
- package/src/components/Snippet/Snippet.stories.tsx +0 -286
- package/src/components/Snippet/Snippet.test.tsx +0 -176
- package/src/components/Snippet/Snippet.tsx +0 -148
- package/src/components/Snippet/__snapshots__/Snippet.test.tsx.snap +0 -603
- package/src/components/StartPanel/StartPanel.css +0 -273
- package/src/components/StartPanel/StartPanel.stories.tsx +0 -404
- package/src/components/StartPanel/StartPanel.test.tsx +0 -360
- package/src/components/StartPanel/StartPanel.tsx +0 -426
- package/src/components/StartPanel/__snapshots__/StartPanel.test.tsx.snap +0 -1986
- package/src/components/Typing/Typing.stories.tsx +0 -130
- package/src/components/Typing/Typing.test.tsx +0 -62
- package/src/components/Typing/Typing.tsx +0 -154
- package/src/components/Typing/__snapshots__/Typing.test.tsx.snap +0 -133
- package/src/components/UploadButton/UploadButton.css +0 -542
- package/src/components/UploadButton/UploadButton.stories.tsx +0 -376
- package/src/components/UploadButton/UploadButton.test.tsx +0 -11
- package/src/components/UploadButton/UploadButton.tsx +0 -688
- package/src/components/UploadButton/UploadDocuments/UploadDocuments.tsx +0 -421
- package/src/components/UploadButton/UploadImages/UploadImages.tsx +0 -538
- package/src/components/UploadButton/__snapshots__/UploadButton.test.tsx.snap +0 -165
- package/src/components/VenueWidget/VenueWidget.css +0 -163
- package/src/components/VenueWidget/VenueWidget.stories.tsx +0 -66
- package/src/components/VenueWidget/VenueWidget.test.tsx +0 -49
- package/src/components/VenueWidget/VenueWidget.tsx +0 -458
- package/src/components/VenueWidget/__snapshots__/VenueWidget.test.tsx.snap +0 -370
- package/src/components/WhyThisAnswer/WhyThisAnswer.css +0 -171
- package/src/components/WhyThisAnswer/WhyThisAnswer.stories.tsx +0 -285
- package/src/components/WhyThisAnswer/WhyThisAnswer.test.tsx +0 -245
- package/src/components/WhyThisAnswer/WhyThisAnswer.tsx +0 -233
- package/src/components/WhyThisAnswer/__snapshots__/WhyThisAnswer.test.tsx.snap +0 -27
- package/src/components/icons/AI.tsx +0 -34
- package/src/components/icons/Alert.tsx +0 -31
- package/src/components/icons/ArrowUp.tsx +0 -28
- package/src/components/icons/Bug.tsx +0 -81
- package/src/components/icons/Chat.tsx +0 -30
- package/src/components/icons/ChevronDown.tsx +0 -26
- package/src/components/icons/ChevronLeft.tsx +0 -28
- package/src/components/icons/ChevronRight.tsx +0 -28
- package/src/components/icons/ChevronUp.tsx +0 -24
- package/src/components/icons/Clear.tsx +0 -23
- package/src/components/icons/Close.tsx +0 -30
- package/src/components/icons/Code.tsx +0 -24
- package/src/components/icons/Copy.tsx +0 -23
- package/src/components/icons/DeepThought.tsx +0 -38
- package/src/components/icons/Delete.tsx +0 -23
- package/src/components/icons/Document.tsx +0 -50
- package/src/components/icons/Download.tsx +0 -23
- package/src/components/icons/Edit.tsx +0 -17
- package/src/components/icons/Expand.tsx +0 -23
- package/src/components/icons/Eye.tsx +0 -17
- package/src/components/icons/EyeInvisible.tsx +0 -24
- package/src/components/icons/Facebook.tsx +0 -23
- package/src/components/icons/Feedback.tsx +0 -24
- package/src/components/icons/File.tsx +0 -17
- package/src/components/icons/FileExcel.tsx +0 -23
- package/src/components/icons/FilePdf.tsx +0 -23
- package/src/components/icons/FileWord.tsx +0 -23
- package/src/components/icons/Fullscreen.tsx +0 -23
- package/src/components/icons/FullscreenExit.tsx +0 -23
- package/src/components/icons/GasStation.tsx +0 -36
- package/src/components/icons/Group.tsx +0 -30
- package/src/components/icons/History.tsx +0 -33
- package/src/components/icons/Image.tsx +0 -37
- package/src/components/icons/Info.tsx +0 -31
- package/src/components/icons/Link.tsx +0 -17
- package/src/components/icons/Linkedin.tsx +0 -23
- package/src/components/icons/Loading.tsx +0 -28
- package/src/components/icons/Logout.tsx +0 -27
- package/src/components/icons/Mail.tsx +0 -17
- package/src/components/icons/MapMarker.tsx +0 -26
- package/src/components/icons/MenuHorizontal.tsx +0 -29
- package/src/components/icons/MenuVertical.tsx +0 -29
- package/src/components/icons/Message.tsx +0 -23
- package/src/components/icons/Microphone.tsx +0 -23
- package/src/components/icons/Minus.tsx +0 -23
- package/src/components/icons/MinusCircle.tsx +0 -24
- package/src/components/icons/PaperClip.tsx +0 -23
- package/src/components/icons/Picture.tsx +0 -23
- package/src/components/icons/Plus.tsx +0 -18
- package/src/components/icons/Preview.tsx +0 -29
- package/src/components/icons/Print.tsx +0 -38
- package/src/components/icons/QuestionHelp.tsx +0 -30
- package/src/components/icons/Refresh.tsx +0 -30
- package/src/components/icons/SelectIcon.tsx +0 -28
- package/src/components/icons/Send.tsx +0 -17
- package/src/components/icons/Setting.tsx +0 -23
- package/src/components/icons/Share.tsx +0 -23
- package/src/components/icons/Sound.tsx +0 -23
- package/src/components/icons/SoundDeactivated.tsx +0 -27
- package/src/components/icons/Telegram.tsx +0 -26
- package/src/components/icons/ThumbDown.tsx +0 -29
- package/src/components/icons/ThumbUp.tsx +0 -29
- package/src/components/icons/Translation.tsx +0 -24
- package/src/components/icons/Twitter.tsx +0 -23
- package/src/components/icons/Upload.tsx +0 -34
- package/src/components/icons/User.tsx +0 -17
- package/src/components/icons/Warning.tsx +0 -24
- package/src/components/icons/WhatsApp.tsx +0 -24
- package/src/components/icons/icons.stories.css +0 -5
- package/src/components/icons/icons.stories.tsx +0 -81
- package/src/components/icons/loading.css +0 -14
- package/src/components/layouts/Chat.test.tsx +0 -40
- package/src/components/layouts/Chat.tsx +0 -69
- package/src/components/layouts/FullBody/FullBody.stories.tsx +0 -110
- package/src/components/layouts/FullPage.test.tsx +0 -65
- package/src/components/layouts/FullPage.tsx +0 -80
- package/src/components/layouts/HiddenChat.test.tsx +0 -40
- package/src/components/layouts/HiddenChat.tsx +0 -233
- package/src/components/layouts/Totem/Totem.stories.tsx +0 -131
- package/src/components/layouts/Totem.test.tsx +0 -40
- package/src/components/layouts/Totem.tsx +0 -49
- package/src/components/layouts/WebsiteAssistant.test.tsx +0 -40
- package/src/components/layouts/WebsiteAssistant.tsx +0 -132
- package/src/components/layouts/ZoomedFullBody/ZoomedFullBody.stories.tsx +0 -131
- package/src/components/layouts/ZoomedFullBody.test.tsx +0 -40
- package/src/components/layouts/ZoomedFullBody.tsx +0 -81
- package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +0 -457
- package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +0 -1045
- package/src/components/layouts/__snapshots__/HiddenChat.test.tsx.snap +0 -460
- package/src/components/layouts/__snapshots__/Totem.test.tsx.snap +0 -566
- package/src/components/layouts/__snapshots__/WebsiteAssistant.test.tsx.snap +0 -59
- package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +0 -523
- package/src/components/layouts/chat.css +0 -451
- package/src/components/layouts/hidden-chat.css +0 -276
- package/src/components/layouts/layouts.stories.tsx +0 -231
- package/src/components/layouts/totem.css +0 -221
- package/src/components/layouts/website-assistant.css +0 -677
- package/src/components/layouts/zoomed-full-body.css +0 -18
- package/src/components/ui/Alert.css +0 -93
- package/src/components/ui/Alert.stories.tsx +0 -137
- package/src/components/ui/Alert.test.tsx +0 -129
- package/src/components/ui/Alert.tsx +0 -123
- package/src/components/ui/Button.css +0 -167
- package/src/components/ui/Button.stories.tsx +0 -156
- package/src/components/ui/Button.test.tsx +0 -74
- package/src/components/ui/Button.tsx +0 -107
- package/src/components/ui/Card.css +0 -65
- package/src/components/ui/Card.stories.tsx +0 -95
- package/src/components/ui/Card.test.tsx +0 -47
- package/src/components/ui/Card.tsx +0 -48
- package/src/components/ui/Checkbox.css +0 -116
- package/src/components/ui/Checkbox.stories.tsx +0 -74
- package/src/components/ui/Checkbox.test.tsx +0 -28
- package/src/components/ui/Checkbox.tsx +0 -58
- package/src/components/ui/ConfirmDialog.css +0 -42
- package/src/components/ui/ConfirmDialog.stories.tsx +0 -216
- package/src/components/ui/ConfirmDialog.test.tsx +0 -124
- package/src/components/ui/ConfirmDialog.tsx +0 -58
- package/src/components/ui/Details.css +0 -66
- package/src/components/ui/Drawer.css +0 -270
- package/src/components/ui/Drawer.stories.tsx +0 -267
- package/src/components/ui/Drawer.test.tsx +0 -130
- package/src/components/ui/Drawer.tsx +0 -293
- package/src/components/ui/Dropdown.css +0 -173
- package/src/components/ui/Dropdown.tsx +0 -63
- package/src/components/ui/Expandable.css +0 -15
- package/src/components/ui/Expandable.stories.tsx +0 -180
- package/src/components/ui/Expandable.test.tsx +0 -107
- package/src/components/ui/Expandable.tsx +0 -121
- package/src/components/ui/Modal.css +0 -98
- package/src/components/ui/Modal.stories.tsx +0 -171
- package/src/components/ui/Modal.test.tsx +0 -111
- package/src/components/ui/Modal.tsx +0 -117
- package/src/components/ui/Select.css +0 -138
- package/src/components/ui/Select.stories.tsx +0 -79
- package/src/components/ui/Select.test.tsx +0 -84
- package/src/components/ui/Select.tsx +0 -79
- package/src/components/ui/Slider.css +0 -192
- package/src/components/ui/Slider.stories.tsx +0 -63
- package/src/components/ui/Slider.tsx +0 -142
- package/src/components/ui/Spin.css +0 -31
- package/src/components/ui/Spin.stories.tsx +0 -64
- package/src/components/ui/Spin.test.tsx +0 -41
- package/src/components/ui/Spin.tsx +0 -31
- package/src/components/ui/Table.css +0 -132
- package/src/components/ui/Tooltip.css +0 -171
- package/src/components/ui/Tooltip.stories.tsx +0 -109
- package/src/components/ui/Tooltip.test.tsx +0 -102
- package/src/components/ui/Tooltip.tsx +0 -163
- package/src/components/ui/__snapshots__/Alert.test.tsx.snap +0 -59
- package/src/components/ui/__snapshots__/Button.test.tsx.snap +0 -204
- package/src/components/ui/__snapshots__/Card.test.tsx.snap +0 -321
- package/src/components/ui/__snapshots__/Checkbox.test.tsx.snap +0 -131
- package/src/components/ui/__snapshots__/ConfirmDialog.test.tsx.snap +0 -35
- package/src/components/ui/__snapshots__/Drawer.test.tsx.snap +0 -67
- package/src/components/ui/__snapshots__/Expandable.test.tsx.snap +0 -159
- package/src/components/ui/__snapshots__/Modal.test.tsx.snap +0 -59
- package/src/components/ui/__snapshots__/Select.test.tsx.snap +0 -254
- package/src/components/ui/__snapshots__/Spin.test.tsx.snap +0 -193
- package/src/components/ui/__snapshots__/Tooltip.test.tsx.snap +0 -113
- package/src/components/ui/definitions.stories.tsx +0 -139
- package/src/context/visemeContext.tsx +0 -384
- package/src/helpers/chatPdfExport.ts +0 -544
- package/src/helpers/configuration.ts +0 -42
- package/src/helpers/constants.ts +0 -268
- package/src/helpers/convertDocument.test.ts +0 -67
- package/src/helpers/convertDocument.ts +0 -35
- package/src/helpers/credits.ts +0 -54
- package/src/helpers/error.test.ts +0 -29
- package/src/helpers/error.ts +0 -278
- package/src/helpers/imageCompression.ts +0 -230
- package/src/helpers/llmUsage.ts +0 -328
- package/src/helpers/markedExtendedTables.js +0 -217
- package/src/helpers/media.test.ts +0 -169
- package/src/helpers/media.ts +0 -81
- package/src/helpers/message.test.ts +0 -126
- package/src/helpers/message.ts +0 -219
- package/src/helpers/nats/getNatsConfig.ts +0 -148
- package/src/helpers/nats/isSessionExpiredError.test.ts +0 -38
- package/src/helpers/nats/isSessionExpiredError.ts +0 -51
- package/src/helpers/nats/useNats.test.tsx +0 -81
- package/src/helpers/nats/useNats.ts +0 -204
- package/src/helpers/nats/useNatsSession.test.ts +0 -16
- package/src/helpers/nats/useNatsSession.ts +0 -460
- package/src/helpers/piiDetection.test.ts +0 -186
- package/src/helpers/piiDetection.ts +0 -91
- package/src/helpers/sanitizer.ts +0 -17
- package/src/helpers/statistics.ts +0 -58
- package/src/helpers/stt/useSTT.ts +0 -515
- package/src/helpers/translations.ts +0 -39
- package/src/helpers/tts/ttsVoiceUtility.ts +0 -275
- package/src/helpers/tts/useTTS.ts +0 -621
- package/src/helpers/utils.test.ts +0 -331
- package/src/helpers/utils.ts +0 -591
- package/src/helpers/venue.ts +0 -36
- package/src/i18n.ts +0 -39
- package/src/index.stories.tsx +0 -220
- package/src/index.test.tsx +0 -156
- package/src/index.tsx +0 -592
- package/src/locales/de.json +0 -680
- package/src/locales/en.json +0 -754
- package/src/locales/es.json +0 -682
- package/src/locales/fr.json +0 -682
- package/src/locales/it.json +0 -750
- package/src/mocks/data.ts +0 -790
- package/src/styles.css +0 -126
- package/src/types/integration.test.ts +0 -32
- package/src/types/integration.ts +0 -31
- package/src/types/layout.ts +0 -50
- package/src/version.ts +0 -2
|
@@ -1,3930 +0,0 @@
|
|
|
1
|
-
// Typings
|
|
2
|
-
import {
|
|
3
|
-
DialogState,
|
|
4
|
-
Memori,
|
|
5
|
-
Integration,
|
|
6
|
-
Venue,
|
|
7
|
-
Message,
|
|
8
|
-
Medium,
|
|
9
|
-
OpenSession,
|
|
10
|
-
MemoriConfig,
|
|
11
|
-
TranslatedHint,
|
|
12
|
-
Tenant,
|
|
13
|
-
MemoriSession,
|
|
14
|
-
User,
|
|
15
|
-
ExpertReference,
|
|
16
|
-
ResponseSpec,
|
|
17
|
-
ChatLog,
|
|
18
|
-
} from '@memori.ai/memori-api-client/src/types';
|
|
19
|
-
import { ArtifactData } from '../MemoriArtifactSystem/types/artifact.types';
|
|
20
|
-
import { ArtifactAPIBridge } from '../MemoriArtifactSystem/utils/ArtifactAPI';
|
|
21
|
-
import type { LayoutName, PiiDetectionConfig } from '../../types/layout';
|
|
22
|
-
import { checkPii } from '../../helpers/piiDetection'; // PII check when integrationConfig.layout has piiDetection.enabled
|
|
23
|
-
|
|
24
|
-
// Libraries
|
|
25
|
-
import React, {
|
|
26
|
-
useState,
|
|
27
|
-
useEffect,
|
|
28
|
-
useCallback,
|
|
29
|
-
CSSProperties,
|
|
30
|
-
useRef,
|
|
31
|
-
useMemo,
|
|
32
|
-
} from 'react';
|
|
33
|
-
import { useTranslation } from 'react-i18next';
|
|
34
|
-
import memoriApiClient from '@memori.ai/memori-api-client';
|
|
35
|
-
import { IAudioContext } from 'standardized-audio-context';
|
|
36
|
-
import cx from 'classnames';
|
|
37
|
-
import { DateTime } from 'luxon';
|
|
38
|
-
import toast from 'react-hot-toast';
|
|
39
|
-
|
|
40
|
-
// Components
|
|
41
|
-
import PositionDrawer from '../PositionDrawer/PositionDrawer';
|
|
42
|
-
import MemoriAuth from '../Auth/Auth';
|
|
43
|
-
import Chat, { Props as ChatProps } from '../Chat/Chat';
|
|
44
|
-
import StartPanel, { Props as StartPanelProps } from '../StartPanel/StartPanel';
|
|
45
|
-
import Avatar, { Props as AvatarProps } from '../Avatar/Avatar';
|
|
46
|
-
import Header, { Props as HeaderProps } from '../Header/Header';
|
|
47
|
-
import PoweredBy from '../PoweredBy/PoweredBy';
|
|
48
|
-
import AgeVerificationModal from '../AgeVerificationModal/AgeVerificationModal';
|
|
49
|
-
import SettingsDrawer from '../SettingsDrawer/SettingsDrawer';
|
|
50
|
-
import KnownFacts from '../KnownFacts/KnownFacts';
|
|
51
|
-
import ExpertsDrawer from '../ExpertsDrawer/ExpertsDrawer';
|
|
52
|
-
import LoginDrawer from '../LoginDrawer/LoginDrawer';
|
|
53
|
-
import Button from '../ui/Button';
|
|
54
|
-
import CloseIcon from '../icons/Close';
|
|
55
|
-
|
|
56
|
-
// Layout
|
|
57
|
-
import FullPageLayout from '../layouts/FullPage';
|
|
58
|
-
import TotemLayout from '../layouts/Totem';
|
|
59
|
-
import ChatLayout from '../layouts/Chat';
|
|
60
|
-
import WebsiteAssistantLayout from '../layouts/WebsiteAssistant';
|
|
61
|
-
import HiddenChatLayout from '../layouts/HiddenChat';
|
|
62
|
-
import ZoomedFullBodyLayout from '../layouts/ZoomedFullBody';
|
|
63
|
-
|
|
64
|
-
// Helpers / Utils
|
|
65
|
-
import { getTranslation } from '../../helpers/translations';
|
|
66
|
-
import {
|
|
67
|
-
setLocalConfig,
|
|
68
|
-
getLocalConfig,
|
|
69
|
-
removeLocalConfig,
|
|
70
|
-
} from '../../helpers/configuration';
|
|
71
|
-
import {
|
|
72
|
-
hasTouchscreen,
|
|
73
|
-
stripDuplicates,
|
|
74
|
-
installMathJax,
|
|
75
|
-
} from '../../helpers/utils';
|
|
76
|
-
import { getTTSVoice } from '../../helpers/tts/ttsVoiceUtility';
|
|
77
|
-
import {
|
|
78
|
-
allowedMediaTypes,
|
|
79
|
-
anonTag,
|
|
80
|
-
uiLanguages,
|
|
81
|
-
} from '../../helpers/constants';
|
|
82
|
-
import { getErrori18nKey } from '../../helpers/error';
|
|
83
|
-
import { getCredits } from '../../helpers/credits';
|
|
84
|
-
import { sanitizeText } from '../../helpers/sanitizer';
|
|
85
|
-
import { TTSConfig, useTTS } from '../../helpers/tts/useTTS';
|
|
86
|
-
import ChatHistoryDrawer from '../ChatHistoryDrawer/ChatHistory';
|
|
87
|
-
import { STTConfig, useSTT } from '../../helpers/stt/useSTT';
|
|
88
|
-
import { useNats } from '../../helpers/nats/useNats';
|
|
89
|
-
import {
|
|
90
|
-
NatsProgressEvent,
|
|
91
|
-
NatsDialogResponseEvent,
|
|
92
|
-
NatsErrorEvent,
|
|
93
|
-
} from '../../helpers/nats/useNatsSession';
|
|
94
|
-
import {
|
|
95
|
-
isSessionExpiredNatsError,
|
|
96
|
-
isSessionExpiredNatsResponse,
|
|
97
|
-
} from '../../helpers/nats/isSessionExpiredError';
|
|
98
|
-
|
|
99
|
-
// Widget utilities and helpers
|
|
100
|
-
const getMemoriState = (integrationId?: string): object | null => {
|
|
101
|
-
let widget = integrationId
|
|
102
|
-
? document.querySelector(
|
|
103
|
-
`.memori-widget[data-memori-integration="${integrationId}"]`
|
|
104
|
-
) ||
|
|
105
|
-
document
|
|
106
|
-
.querySelector('memori-client')
|
|
107
|
-
?.shadowRoot?.querySelector(`.memori-widget[data-memori-integration]`)
|
|
108
|
-
: document.querySelector('.memori-widget') ||
|
|
109
|
-
document
|
|
110
|
-
.querySelector('memori-client')
|
|
111
|
-
?.shadowRoot?.querySelector('.memori-widget');
|
|
112
|
-
|
|
113
|
-
if (!widget) return null;
|
|
114
|
-
|
|
115
|
-
let engineState = (widget as HTMLElement).dataset?.memoriEngineState;
|
|
116
|
-
if (!engineState) return null;
|
|
117
|
-
|
|
118
|
-
let dialogState = JSON.parse(engineState);
|
|
119
|
-
|
|
120
|
-
let loginToken = getLocalConfig<string | undefined>('loginToken', undefined);
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
...dialogState,
|
|
124
|
-
loginToken,
|
|
125
|
-
};
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/** Unified login token resolution: React state > ref > call-site override > props. */
|
|
129
|
-
const resolveLoginTokenValue = (
|
|
130
|
-
loginToken: string | undefined,
|
|
131
|
-
userTokenRef: string | undefined,
|
|
132
|
-
callAdditionalInfoLoginToken: string | undefined,
|
|
133
|
-
additionalInfoLoginToken: string | undefined,
|
|
134
|
-
authTokenProp: string | undefined
|
|
135
|
-
): string | undefined =>
|
|
136
|
-
loginToken ??
|
|
137
|
-
userTokenRef ??
|
|
138
|
-
callAdditionalInfoLoginToken ??
|
|
139
|
-
additionalInfoLoginToken ??
|
|
140
|
-
authTokenProp;
|
|
141
|
-
|
|
142
|
-
/** Place spec with all nulls for postEnterTextAsync when position is not set or user chose "I don't want to provide my position". */
|
|
143
|
-
const NULL_PLACE_SPEC = {
|
|
144
|
-
placeName: null,
|
|
145
|
-
latitude: null,
|
|
146
|
-
longitude: null,
|
|
147
|
-
uncertaintyKm: null,
|
|
148
|
-
} as const;
|
|
149
|
-
|
|
150
|
-
const logWidgetError = (context: string, detail?: unknown) => {
|
|
151
|
-
console.error(`[MemoriWidget] ${context}`, detail ?? '');
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
/** Reads correlation id from HTTP async response (supports camelCase / snake_case). */
|
|
155
|
-
function readCorrelationID(response: {
|
|
156
|
-
correlationID?: string;
|
|
157
|
-
}): string | undefined {
|
|
158
|
-
const value = response.correlationID;
|
|
159
|
-
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/** Stable engine-owned fields used to tell whether a pending turn completed. */
|
|
163
|
-
export function dialogStateFingerprint(state: DialogState | undefined): string {
|
|
164
|
-
if (!state) return '';
|
|
165
|
-
return JSON.stringify({
|
|
166
|
-
state: state.state,
|
|
167
|
-
previousState: state.previousState,
|
|
168
|
-
emission: state.emission,
|
|
169
|
-
emitter: state.emitter,
|
|
170
|
-
lastMatchedMemoryID: state.lastMatchedMemoryID,
|
|
171
|
-
currentDate: state.currentDate,
|
|
172
|
-
currentMemoryID: state.currentMemoryID,
|
|
173
|
-
contextVars: state.contextVars,
|
|
174
|
-
emittedMedia: state.emittedMedia,
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
type MemoriTextEnteredEvent = CustomEvent<{
|
|
179
|
-
text: string;
|
|
180
|
-
waitForPrevious?: boolean;
|
|
181
|
-
hidden?: boolean;
|
|
182
|
-
typingText?: string;
|
|
183
|
-
useLoaderTextAsMsg?: boolean;
|
|
184
|
-
hasBatchQueued?: boolean;
|
|
185
|
-
}>;
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Dispatches a MemoriTextEntered event to simulate a user typing a message
|
|
189
|
-
* @param message The text message to send
|
|
190
|
-
* @param waitForPrevious Whether to wait for previous message to finish before sending (default true)
|
|
191
|
-
* @param hidden Whether to hide the message from chat history (default false)
|
|
192
|
-
* @param typingText Optional custom typing indicator text
|
|
193
|
-
* @param useLoaderTextAsMsg Whether to use the loader text as the message (default false)
|
|
194
|
-
* @param hasBatchQueued Whether there are more messages queued to be sent (default false)
|
|
195
|
-
*/
|
|
196
|
-
const typeMessage = (
|
|
197
|
-
message: string,
|
|
198
|
-
waitForPrevious = true,
|
|
199
|
-
hidden = false,
|
|
200
|
-
typingText?: string,
|
|
201
|
-
useLoaderTextAsMsg = false,
|
|
202
|
-
hasBatchQueued = false
|
|
203
|
-
) => {
|
|
204
|
-
const e: MemoriTextEnteredEvent = new CustomEvent('MemoriTextEntered', {
|
|
205
|
-
detail: {
|
|
206
|
-
text: message,
|
|
207
|
-
waitForPrevious,
|
|
208
|
-
hidden,
|
|
209
|
-
typingText,
|
|
210
|
-
useLoaderTextAsMsg,
|
|
211
|
-
hasBatchQueued,
|
|
212
|
-
},
|
|
213
|
-
});
|
|
214
|
-
document.dispatchEvent(e);
|
|
215
|
-
|
|
216
|
-
// Special handling for Safari on iOS devices
|
|
217
|
-
const isSafariIOS =
|
|
218
|
-
window.navigator.userAgent.includes('Safari') &&
|
|
219
|
-
!window.navigator.userAgent.includes('Chrome') &&
|
|
220
|
-
/iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
221
|
-
|
|
222
|
-
if (isSafariIOS) {
|
|
223
|
-
// Dispatch end speak event after short delay for iOS Safari
|
|
224
|
-
setTimeout(() => {
|
|
225
|
-
document.dispatchEvent(new CustomEvent('MemoriEndSpeak'));
|
|
226
|
-
}, 300);
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Helper function to send a hidden message
|
|
232
|
-
* Wraps typeMessage with hidden=true and passes through other params
|
|
233
|
-
*/
|
|
234
|
-
const typeMessageHidden = (
|
|
235
|
-
message: string,
|
|
236
|
-
waitForPrevious = true,
|
|
237
|
-
typingText?: string,
|
|
238
|
-
useLoaderTextAsMsg = false,
|
|
239
|
-
hasBatchQueued = false
|
|
240
|
-
) =>
|
|
241
|
-
typeMessage(
|
|
242
|
-
message,
|
|
243
|
-
waitForPrevious,
|
|
244
|
-
true,
|
|
245
|
-
typingText,
|
|
246
|
-
useLoaderTextAsMsg,
|
|
247
|
-
hasBatchQueued
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
const typeBatchMessages = (
|
|
251
|
-
messages: {
|
|
252
|
-
message: string;
|
|
253
|
-
waitForPrevious?: boolean;
|
|
254
|
-
hidden?: boolean;
|
|
255
|
-
typingText?: string;
|
|
256
|
-
useLoaderTextAsMsg?: boolean;
|
|
257
|
-
}[]
|
|
258
|
-
) => {
|
|
259
|
-
function disableInputs() {
|
|
260
|
-
document
|
|
261
|
-
.querySelector('fieldset#chat-fieldset')
|
|
262
|
-
?.setAttribute('disabled', '');
|
|
263
|
-
|
|
264
|
-
const styles = `opacity: 0.5; touch-action: none; pointer-events: none;`;
|
|
265
|
-
document
|
|
266
|
-
.querySelector('textarea.memori-chat-textarea--input')
|
|
267
|
-
?.setAttribute('style', styles);
|
|
268
|
-
document
|
|
269
|
-
.querySelector('button.memori-chat-inputs--send')
|
|
270
|
-
?.setAttribute('style', styles);
|
|
271
|
-
document
|
|
272
|
-
.querySelector('button.memori-chat-inputs--mic')
|
|
273
|
-
?.setAttribute('style', styles);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function reEnableInputs() {
|
|
277
|
-
document
|
|
278
|
-
.querySelector('fieldset#chat-fieldset')
|
|
279
|
-
?.removeAttribute('disabled');
|
|
280
|
-
|
|
281
|
-
document
|
|
282
|
-
.querySelector('textarea.memori-chat-textarea--input')
|
|
283
|
-
?.removeAttribute('style');
|
|
284
|
-
document
|
|
285
|
-
.querySelector('button.memori-chat-inputs--send')
|
|
286
|
-
?.removeAttribute('style');
|
|
287
|
-
document
|
|
288
|
-
.querySelector('button.memori-chat-inputs--mic')
|
|
289
|
-
?.removeAttribute('style');
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function areInputsDisabled() {
|
|
293
|
-
return !!document
|
|
294
|
-
.querySelector('fieldset#chat-fieldset')
|
|
295
|
-
?.hasAttribute('disabled');
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const isSafariIOS =
|
|
299
|
-
window.navigator.userAgent.includes('Safari') &&
|
|
300
|
-
!window.navigator.userAgent.includes('Chrome') &&
|
|
301
|
-
/iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
302
|
-
|
|
303
|
-
const stepsGenerator = (function* () {
|
|
304
|
-
yield* messages;
|
|
305
|
-
})();
|
|
306
|
-
|
|
307
|
-
disableInputs();
|
|
308
|
-
|
|
309
|
-
const submitNewMessage = () => {
|
|
310
|
-
const next = stepsGenerator.next();
|
|
311
|
-
const step = next.value;
|
|
312
|
-
|
|
313
|
-
if (step) {
|
|
314
|
-
if (!areInputsDisabled()) {
|
|
315
|
-
disableInputs();
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
let waitForPrevious = step.waitForPrevious;
|
|
319
|
-
if (isSafariIOS) waitForPrevious = false;
|
|
320
|
-
|
|
321
|
-
typeMessage(
|
|
322
|
-
step.message,
|
|
323
|
-
waitForPrevious,
|
|
324
|
-
step.hidden,
|
|
325
|
-
step.typingText,
|
|
326
|
-
step.useLoaderTextAsMsg,
|
|
327
|
-
!next.done
|
|
328
|
-
);
|
|
329
|
-
|
|
330
|
-
if (isSafariIOS) {
|
|
331
|
-
setTimeout(() => {
|
|
332
|
-
document.dispatchEvent(new CustomEvent('MemoriEndSpeak'));
|
|
333
|
-
reEnableInputs();
|
|
334
|
-
}, 3000);
|
|
335
|
-
}
|
|
336
|
-
} else if (areInputsDisabled()) {
|
|
337
|
-
reEnableInputs();
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (next.done) {
|
|
341
|
-
document.removeEventListener('MemoriEndSpeak', submitNewMessage);
|
|
342
|
-
if (areInputsDisabled()) reEnableInputs();
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
document.addEventListener('MemoriEndSpeak', submitNewMessage);
|
|
348
|
-
|
|
349
|
-
submitNewMessage();
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
type MemoriNewDialogStateEvent = CustomEvent<DialogState>;
|
|
353
|
-
|
|
354
|
-
type ArtifactCreatedEvent = CustomEvent<{
|
|
355
|
-
artifact: ArtifactData;
|
|
356
|
-
message: Message;
|
|
357
|
-
}>;
|
|
358
|
-
|
|
359
|
-
interface CustomEventMap {
|
|
360
|
-
MemoriTextEntered: MemoriTextEnteredEvent;
|
|
361
|
-
MemoriEndSpeak: CustomEvent;
|
|
362
|
-
MemoriResetUIEffects: CustomEvent;
|
|
363
|
-
MemoriNewDialogState: MemoriNewDialogStateEvent;
|
|
364
|
-
artifactCreated: ArtifactCreatedEvent;
|
|
365
|
-
}
|
|
366
|
-
declare global {
|
|
367
|
-
interface Document {
|
|
368
|
-
addEventListener<K extends keyof CustomEventMap>(
|
|
369
|
-
type: K,
|
|
370
|
-
listener: (this: Document, ev: CustomEventMap[K]) => void
|
|
371
|
-
): void;
|
|
372
|
-
removeEventListener<K extends keyof CustomEventMap>(
|
|
373
|
-
type: K,
|
|
374
|
-
listener: (this: Document, ev: CustomEventMap[K]) => void
|
|
375
|
-
): void;
|
|
376
|
-
dispatchEvent<K extends keyof CustomEventMap>(ev: CustomEventMap[K]): void;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
interface Window {
|
|
380
|
-
getMemoriState: typeof getMemoriState;
|
|
381
|
-
typeMessage: typeof typeMessage;
|
|
382
|
-
typeMessageHidden: typeof typeMessageHidden;
|
|
383
|
-
typeBatchMessages: typeof typeBatchMessages;
|
|
384
|
-
MemoriArtifactAPI?: {
|
|
385
|
-
openArtifact: (artifact: ArtifactData) => void;
|
|
386
|
-
createAndOpenArtifact: (
|
|
387
|
-
content: string,
|
|
388
|
-
mimeType?: string,
|
|
389
|
-
title?: string
|
|
390
|
-
) => void;
|
|
391
|
-
createFromOutputElement: (outputElement: HTMLOutputElement) => string;
|
|
392
|
-
closeArtifact: () => void;
|
|
393
|
-
toggleFullscreen: () => void;
|
|
394
|
-
getState: () => {
|
|
395
|
-
currentArtifact: ArtifactData | null;
|
|
396
|
-
isDrawerOpen: boolean;
|
|
397
|
-
isFullscreen: boolean;
|
|
398
|
-
};
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
window.getMemoriState = getMemoriState;
|
|
403
|
-
window.typeMessage = typeMessage;
|
|
404
|
-
window.typeMessageHidden = typeMessageHidden;
|
|
405
|
-
window.typeBatchMessages = typeBatchMessages;
|
|
406
|
-
|
|
407
|
-
let audioContext: IAudioContext;
|
|
408
|
-
|
|
409
|
-
let memoriPassword: string | undefined;
|
|
410
|
-
|
|
411
|
-
export interface LayoutProps {
|
|
412
|
-
Header?: typeof Header;
|
|
413
|
-
headerProps?: HeaderProps;
|
|
414
|
-
Avatar: typeof Avatar;
|
|
415
|
-
avatarProps?: AvatarProps;
|
|
416
|
-
Chat?: typeof Chat;
|
|
417
|
-
chatProps?: ChatProps;
|
|
418
|
-
StartPanel: typeof StartPanel;
|
|
419
|
-
startPanelProps?: StartPanelProps;
|
|
420
|
-
integrationStyle?: JSX.Element | null;
|
|
421
|
-
integrationBackground?: JSX.Element | null;
|
|
422
|
-
poweredBy?: JSX.Element | null;
|
|
423
|
-
sessionId?: string;
|
|
424
|
-
hasUserActivatedSpeak?: boolean;
|
|
425
|
-
showUpload?: boolean;
|
|
426
|
-
loading?: boolean;
|
|
427
|
-
autoStart?: boolean;
|
|
428
|
-
onSidebarToggle?: (isOpen: boolean) => void;
|
|
429
|
-
/** When true or "true" (e.g. from integrationConfig or web component attribute), hide the 3D avatar. */
|
|
430
|
-
avatar3dHidden?: boolean | string;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
export interface Props {
|
|
434
|
-
memori: Memori;
|
|
435
|
-
ownerUserName?: string | null;
|
|
436
|
-
ownerUserID?: string | null;
|
|
437
|
-
tenantID: string;
|
|
438
|
-
memoriConfigs?: MemoriConfig[];
|
|
439
|
-
memoriLang?: string;
|
|
440
|
-
/** UI language: labels, buttons, page translation (i18n) */
|
|
441
|
-
uiLang?: string;
|
|
442
|
-
/** Spoken/chat language: select box in StartPanel and conversation language */
|
|
443
|
-
spokenLang?: string;
|
|
444
|
-
multilingual?: boolean;
|
|
445
|
-
integration?: Integration;
|
|
446
|
-
layout?: LayoutName;
|
|
447
|
-
customLayout?: React.FC<LayoutProps>;
|
|
448
|
-
showShare?: boolean;
|
|
449
|
-
showCopyButton?: boolean;
|
|
450
|
-
showTranslationOriginal?: boolean;
|
|
451
|
-
showInputs?: boolean;
|
|
452
|
-
showDates?: boolean;
|
|
453
|
-
showContextPerLine?: boolean;
|
|
454
|
-
showMessageConsumption?: boolean;
|
|
455
|
-
showSettings?: boolean;
|
|
456
|
-
showClear?: boolean;
|
|
457
|
-
showOnlyLastMessages?: boolean;
|
|
458
|
-
showTypingText?: boolean;
|
|
459
|
-
showLogin?: boolean;
|
|
460
|
-
showUpload?: boolean;
|
|
461
|
-
showChatHistory?: boolean;
|
|
462
|
-
showReasoning?: boolean;
|
|
463
|
-
/** When true and layout is WEBSITE_ASSISTANT, hide the 3D avatar in the expanded panel. */
|
|
464
|
-
avatar3dHidden?: boolean;
|
|
465
|
-
preview?: boolean;
|
|
466
|
-
embed?: boolean;
|
|
467
|
-
height?: number | string;
|
|
468
|
-
secret?: string;
|
|
469
|
-
baseUrl?: string;
|
|
470
|
-
apiURL?: string;
|
|
471
|
-
engineURL?: string;
|
|
472
|
-
initialContextVars?: { [key: string]: string };
|
|
473
|
-
initialQuestion?: string;
|
|
474
|
-
ogImage?: string;
|
|
475
|
-
sessionID?: string;
|
|
476
|
-
tenant?: Tenant;
|
|
477
|
-
personification?: {
|
|
478
|
-
name?: string;
|
|
479
|
-
tag: string;
|
|
480
|
-
pin: string;
|
|
481
|
-
};
|
|
482
|
-
ttsProvider?: 'azure' | 'openai';
|
|
483
|
-
enableAudio?: boolean;
|
|
484
|
-
defaultSpeakerActive?: boolean;
|
|
485
|
-
disableTextEnteredEvents?: boolean;
|
|
486
|
-
onStateChange?: (state?: DialogState) => void;
|
|
487
|
-
additionalInfo?: OpenSession['additionalInfo'] & { [key: string]: string };
|
|
488
|
-
customMediaRenderer?: ChatProps['customMediaRenderer'];
|
|
489
|
-
additionalSettings?: JSX.Element | null;
|
|
490
|
-
userAvatar?: string | JSX.Element;
|
|
491
|
-
useMathFormatting?: boolean;
|
|
492
|
-
autoStart?: boolean;
|
|
493
|
-
applyVarsToRoot?: boolean;
|
|
494
|
-
showFunctionCache?: boolean;
|
|
495
|
-
authToken?: string;
|
|
496
|
-
__WEBCOMPONENT__?: boolean;
|
|
497
|
-
/** Override total document payload and per-document content limit (character count). Default from constants. */
|
|
498
|
-
maxTotalMessagePayload?: number;
|
|
499
|
-
/** Max characters in chat textarea; shows counter and enforces paste + existing text does not exceed this limit. */
|
|
500
|
-
maxTextareaCharacters?: number;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
const MemoriWidget = ({
|
|
504
|
-
memori,
|
|
505
|
-
memoriConfigs,
|
|
506
|
-
ownerUserID,
|
|
507
|
-
ownerUserName,
|
|
508
|
-
tenantID,
|
|
509
|
-
memoriLang,
|
|
510
|
-
uiLang,
|
|
511
|
-
spokenLang,
|
|
512
|
-
multilingual,
|
|
513
|
-
integration,
|
|
514
|
-
layout,
|
|
515
|
-
customLayout,
|
|
516
|
-
showShare,
|
|
517
|
-
preview = false,
|
|
518
|
-
embed = false,
|
|
519
|
-
showCopyButton = true,
|
|
520
|
-
showTranslationOriginal = false,
|
|
521
|
-
showInputs = true,
|
|
522
|
-
showDates = false,
|
|
523
|
-
showContextPerLine = false,
|
|
524
|
-
showMessageConsumption = false,
|
|
525
|
-
showSettings,
|
|
526
|
-
showTypingText = false,
|
|
527
|
-
showClear = false,
|
|
528
|
-
showLogin = false,
|
|
529
|
-
showUpload,
|
|
530
|
-
showOnlyLastMessages,
|
|
531
|
-
showChatHistory,
|
|
532
|
-
showReasoning,
|
|
533
|
-
avatar3dHidden,
|
|
534
|
-
height = '100vh',
|
|
535
|
-
secret,
|
|
536
|
-
baseUrl = 'https://aisuru-staging.aclambda.online',
|
|
537
|
-
apiURL = 'https://backend-staging.memori.ai',
|
|
538
|
-
engineURL = 'https://engine-staging.memori.ai',
|
|
539
|
-
initialContextVars,
|
|
540
|
-
initialQuestion,
|
|
541
|
-
ttsProvider,
|
|
542
|
-
ogImage,
|
|
543
|
-
sessionID: initialSessionID,
|
|
544
|
-
tenant,
|
|
545
|
-
personification,
|
|
546
|
-
authToken,
|
|
547
|
-
enableAudio,
|
|
548
|
-
defaultSpeakerActive = true,
|
|
549
|
-
disableTextEnteredEvents = false,
|
|
550
|
-
onStateChange,
|
|
551
|
-
additionalInfo,
|
|
552
|
-
additionalSettings,
|
|
553
|
-
customMediaRenderer,
|
|
554
|
-
userAvatar,
|
|
555
|
-
__WEBCOMPONENT__ = false,
|
|
556
|
-
useMathFormatting = false,
|
|
557
|
-
autoStart = false,
|
|
558
|
-
applyVarsToRoot = false,
|
|
559
|
-
showFunctionCache = false,
|
|
560
|
-
maxTotalMessagePayload,
|
|
561
|
-
maxTextareaCharacters,
|
|
562
|
-
}: Props) => {
|
|
563
|
-
const { t, i18n } = useTranslation();
|
|
564
|
-
|
|
565
|
-
const [isClient, setIsClient] = useState(false);
|
|
566
|
-
useEffect(() => {
|
|
567
|
-
setIsClient(true);
|
|
568
|
-
}, []);
|
|
569
|
-
|
|
570
|
-
// API calls methods
|
|
571
|
-
const client = memoriApiClient(apiURL, engineURL);
|
|
572
|
-
const {
|
|
573
|
-
initSession,
|
|
574
|
-
deleteSession,
|
|
575
|
-
postEnterTextAsync,
|
|
576
|
-
postTextEnteredEvent,
|
|
577
|
-
postPlaceChangedEvent,
|
|
578
|
-
postDateChangedEvent,
|
|
579
|
-
postTagChangedEvent,
|
|
580
|
-
getSession,
|
|
581
|
-
getExpertReferences,
|
|
582
|
-
getSessionChatLogs,
|
|
583
|
-
} = client;
|
|
584
|
-
|
|
585
|
-
const [instruct, setInstruct] = useState(false);
|
|
586
|
-
const [enableFocusChatInput, setEnableFocusChatInput] = useState(true);
|
|
587
|
-
|
|
588
|
-
const [loginToken, setLoginToken] = useState<string | undefined>(
|
|
589
|
-
additionalInfo?.loginToken ?? authToken
|
|
590
|
-
);
|
|
591
|
-
const userTokenRef = useRef<string | undefined>(
|
|
592
|
-
additionalInfo?.loginToken ?? authToken
|
|
593
|
-
);
|
|
594
|
-
const prevPropsLoginTokenRef = useRef<string | undefined>(
|
|
595
|
-
additionalInfo?.loginToken ?? authToken
|
|
596
|
-
);
|
|
597
|
-
|
|
598
|
-
const [user, setUser] = useState<User | undefined>({
|
|
599
|
-
avatarURL: typeof userAvatar === 'string' ? userAvatar : undefined,
|
|
600
|
-
} as User);
|
|
601
|
-
|
|
602
|
-
const resolveLoginToken = useCallback(
|
|
603
|
-
(callAdditionalInfoLoginToken?: string) =>
|
|
604
|
-
resolveLoginTokenValue(
|
|
605
|
-
loginToken,
|
|
606
|
-
userTokenRef.current,
|
|
607
|
-
callAdditionalInfoLoginToken,
|
|
608
|
-
additionalInfo?.loginToken,
|
|
609
|
-
authToken
|
|
610
|
-
),
|
|
611
|
-
[loginToken, additionalInfo?.loginToken, authToken]
|
|
612
|
-
);
|
|
613
|
-
|
|
614
|
-
// Sync loginToken when props change (including explicit removal from parent)
|
|
615
|
-
useEffect(() => {
|
|
616
|
-
const incomingToken = additionalInfo?.loginToken ?? authToken;
|
|
617
|
-
const prevPropsToken = prevPropsLoginTokenRef.current;
|
|
618
|
-
prevPropsLoginTokenRef.current = incomingToken;
|
|
619
|
-
|
|
620
|
-
if (incomingToken !== prevPropsToken) {
|
|
621
|
-
setLoginToken(incomingToken);
|
|
622
|
-
userTokenRef.current = incomingToken;
|
|
623
|
-
if (!incomingToken) {
|
|
624
|
-
removeLocalConfig('loginToken');
|
|
625
|
-
setUser(undefined);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}, [additionalInfo?.loginToken, authToken]);
|
|
629
|
-
|
|
630
|
-
useEffect(() => {
|
|
631
|
-
if (!loginToken || !(showLogin || memori.requireLoginToken)) {
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
let cancelled = false;
|
|
636
|
-
|
|
637
|
-
client.backend
|
|
638
|
-
.pwlGetCurrentUser(loginToken)
|
|
639
|
-
.then(({ user: fetchedUser, resultCode }) => {
|
|
640
|
-
if (cancelled) return;
|
|
641
|
-
|
|
642
|
-
if (fetchedUser && resultCode === 0) {
|
|
643
|
-
setUser(fetchedUser);
|
|
644
|
-
setLocalConfig('loginToken', loginToken);
|
|
645
|
-
userTokenRef.current = loginToken;
|
|
646
|
-
|
|
647
|
-
if (!birthDate && fetchedUser.birthDate) {
|
|
648
|
-
setBirthDate(fetchedUser.birthDate);
|
|
649
|
-
setLocalConfig('birthDate', fetchedUser.birthDate);
|
|
650
|
-
}
|
|
651
|
-
} else {
|
|
652
|
-
setLoginToken(undefined);
|
|
653
|
-
userTokenRef.current = undefined;
|
|
654
|
-
setUser(undefined);
|
|
655
|
-
removeLocalConfig('loginToken');
|
|
656
|
-
}
|
|
657
|
-
});
|
|
658
|
-
|
|
659
|
-
return () => {
|
|
660
|
-
cancelled = true;
|
|
661
|
-
};
|
|
662
|
-
}, [loginToken, showLogin, memori.requireLoginToken]);
|
|
663
|
-
const [showLoginDrawer, setShowLoginDrawer] = useState(false);
|
|
664
|
-
|
|
665
|
-
const [clickedStart, setClickedStart] = useState(false);
|
|
666
|
-
const sessionStartingRef = useRef(false);
|
|
667
|
-
|
|
668
|
-
const language =
|
|
669
|
-
memori.culture?.split('-')?.[0]?.toUpperCase()! ||
|
|
670
|
-
memoriConfigs
|
|
671
|
-
?.find(c => c.memoriConfigID === memori.memoriConfigurationID)
|
|
672
|
-
?.culture?.split('-')?.[0]
|
|
673
|
-
?.toUpperCase()!;
|
|
674
|
-
const integrationConfig = integration?.customData
|
|
675
|
-
? JSON.parse(integration.customData)
|
|
676
|
-
: null;
|
|
677
|
-
|
|
678
|
-
const isMultilanguageEnabled =
|
|
679
|
-
multilingual !== undefined
|
|
680
|
-
? multilingual
|
|
681
|
-
: !!integrationConfig?.multilanguage;
|
|
682
|
-
const forcedTimeout = integrationConfig?.forcedTimeout as number | undefined;
|
|
683
|
-
const [userLang, setUserLang] = useState(
|
|
684
|
-
spokenLang ??
|
|
685
|
-
memoriLang ??
|
|
686
|
-
integrationConfig?.lang ??
|
|
687
|
-
language ??
|
|
688
|
-
integrationConfig?.uiLang ??
|
|
689
|
-
i18n.language ??
|
|
690
|
-
'IT'
|
|
691
|
-
);
|
|
692
|
-
|
|
693
|
-
// Sync userLang when parent passes spokenLang (select box in StartPanel)
|
|
694
|
-
useEffect(() => {
|
|
695
|
-
if (spokenLang != null) {
|
|
696
|
-
setUserLang(spokenLang);
|
|
697
|
-
}
|
|
698
|
-
}, [spokenLang]);
|
|
699
|
-
|
|
700
|
-
const applyMathFormatting =
|
|
701
|
-
useMathFormatting !== undefined
|
|
702
|
-
? useMathFormatting
|
|
703
|
-
: !!integrationConfig?.useMathFormatting;
|
|
704
|
-
useEffect(() => {
|
|
705
|
-
if (applyMathFormatting) installMathJax();
|
|
706
|
-
}, [applyMathFormatting]);
|
|
707
|
-
|
|
708
|
-
/**
|
|
709
|
-
* Sets the UI language in the i18n instance (page translation). uiLang takes precedence.
|
|
710
|
-
*/
|
|
711
|
-
useEffect(() => {
|
|
712
|
-
const langToApply =
|
|
713
|
-
uiLang && uiLanguages.includes(uiLang.toLowerCase())
|
|
714
|
-
? uiLang.toLowerCase()
|
|
715
|
-
: userLang && uiLanguages.includes(userLang.toLowerCase())
|
|
716
|
-
? userLang.toLowerCase()
|
|
717
|
-
: null;
|
|
718
|
-
if (langToApply && typeof i18n?.changeLanguage === 'function') {
|
|
719
|
-
// @ts-ignore
|
|
720
|
-
i18n.changeLanguage(langToApply);
|
|
721
|
-
}
|
|
722
|
-
}, [uiLang, userLang]);
|
|
723
|
-
|
|
724
|
-
const [loading, setLoading] = useState(false);
|
|
725
|
-
const [memoriTyping, setMemoriTyping] = useState<boolean>(false);
|
|
726
|
-
const [typingText, setTypingText] = useState<string>();
|
|
727
|
-
|
|
728
|
-
type EnterTextRetryParams = {
|
|
729
|
-
text?: string;
|
|
730
|
-
media?: Medium[];
|
|
731
|
-
translate?: boolean;
|
|
732
|
-
translatedText?: string;
|
|
733
|
-
hidden?: boolean;
|
|
734
|
-
typingText?: string;
|
|
735
|
-
useLoaderTextAsMsg?: boolean;
|
|
736
|
-
hasBatchQueued?: boolean;
|
|
737
|
-
expiredSessionID?: string;
|
|
738
|
-
continueFromChatLogID?: string;
|
|
739
|
-
};
|
|
740
|
-
|
|
741
|
-
type PendingEnterText = EnterTextRetryParams & {
|
|
742
|
-
msg?: string;
|
|
743
|
-
stateBeforeRequest?: string;
|
|
744
|
-
waitForResponse?: {
|
|
745
|
-
resolve: (event: NatsDialogResponseEvent) => void;
|
|
746
|
-
reject: (error: Error) => void;
|
|
747
|
-
timeoutId: ReturnType<typeof setTimeout>;
|
|
748
|
-
};
|
|
749
|
-
};
|
|
750
|
-
const pendingEnterTextRef = useRef<Map<string, PendingEnterText>>(new Map());
|
|
751
|
-
const bufferedNatsResponsesRef = useRef<Map<string, NatsDialogResponseEvent>>(
|
|
752
|
-
new Map()
|
|
753
|
-
);
|
|
754
|
-
|
|
755
|
-
// Layout: from prop (string only) or integrationConfig. PII detection is only from integrationConfig (customData.layout as object with piiDetection).
|
|
756
|
-
const layoutName =
|
|
757
|
-
typeof layout === 'string'
|
|
758
|
-
? layout
|
|
759
|
-
: typeof integrationConfig?.layout === 'string'
|
|
760
|
-
? integrationConfig.layout
|
|
761
|
-
: integrationConfig?.layout?.name;
|
|
762
|
-
const selectedLayout = layoutName || 'DEFAULT';
|
|
763
|
-
const piiDetection: PiiDetectionConfig | undefined =
|
|
764
|
-
typeof integrationConfig?.layout === 'object' &&
|
|
765
|
-
integrationConfig?.layout !== null &&
|
|
766
|
-
integrationConfig?.layout?.piiDetection?.enabled
|
|
767
|
-
? integrationConfig.layout.piiDetection
|
|
768
|
-
: undefined;
|
|
769
|
-
|
|
770
|
-
const defaultEnableAudio =
|
|
771
|
-
enableAudio ?? integrationConfig?.enableAudio ?? true;
|
|
772
|
-
|
|
773
|
-
const [hasUserActivatedListening, setHasUserActivatedListening] =
|
|
774
|
-
useState(false);
|
|
775
|
-
const [hasUserTypedMessage, setHasUserTypedMessage] = useState(false);
|
|
776
|
-
const [showPositionDrawer, setShowPositionDrawer] = useState(false);
|
|
777
|
-
const [showSettingsDrawer, setShowSettingsDrawer] = useState(false);
|
|
778
|
-
const [showChatHistoryDrawer, setShowChatHistoryDrawer] = useState(false);
|
|
779
|
-
const [showKnownFactsDrawer, setShowKnownFactsDrawer] = useState(false);
|
|
780
|
-
const [showExpertsDrawer, setShowExpertsDrawer] = useState(false);
|
|
781
|
-
const [continuousSpeech, setContinuousSpeech] = useState(false);
|
|
782
|
-
const [continuousSpeechTimeout, setContinuousSpeechTimeout] = useState(2);
|
|
783
|
-
const [controlsPosition, setControlsPosition] = useState<'center' | 'bottom'>(
|
|
784
|
-
'center'
|
|
785
|
-
);
|
|
786
|
-
|
|
787
|
-
const [enablePositionControls, setEnablePositionControls] = useState(false);
|
|
788
|
-
const [avatarType, setAvatarType] = useState<'blob' | 'avatar3d' | null>(
|
|
789
|
-
null
|
|
790
|
-
);
|
|
791
|
-
const [hideEmissions, setHideEmissions] = useState(false);
|
|
792
|
-
const [runtimeShowMessageConsumption, setRuntimeShowMessageConsumption] =
|
|
793
|
-
useState(false);
|
|
794
|
-
|
|
795
|
-
const speechSynthesizerRef = useRef<any | null>(null);
|
|
796
|
-
const [memoriSpeaking, setMemoriSpeaking] = useState(false);
|
|
797
|
-
|
|
798
|
-
useEffect(() => {
|
|
799
|
-
setMemoriSpeaking(!!speechSynthesizerRef.current);
|
|
800
|
-
}, [speechSynthesizerRef.current]);
|
|
801
|
-
|
|
802
|
-
useEffect(() => {
|
|
803
|
-
let defaultControlsPosition: 'center' | 'bottom' = 'bottom';
|
|
804
|
-
let microphoneMode = getLocalConfig<string>(
|
|
805
|
-
'microphoneMode',
|
|
806
|
-
'HOLD_TO_TALK'
|
|
807
|
-
);
|
|
808
|
-
|
|
809
|
-
if (window.innerWidth <= 768) {
|
|
810
|
-
// on mobile, default position is bottom
|
|
811
|
-
defaultControlsPosition = 'bottom';
|
|
812
|
-
// on mobile, keep only HOLD_TO_TALK mode
|
|
813
|
-
microphoneMode = 'HOLD_TO_TALK';
|
|
814
|
-
} else if (
|
|
815
|
-
window.matchMedia('(orientation: portrait)').matches ||
|
|
816
|
-
window.innerHeight > window.innerWidth
|
|
817
|
-
) {
|
|
818
|
-
// on portrait, default position is center
|
|
819
|
-
defaultControlsPosition = 'center';
|
|
820
|
-
} else {
|
|
821
|
-
// on landscape, default position is bottom
|
|
822
|
-
defaultControlsPosition = 'bottom';
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
setContinuousSpeech(speakerMuted ? false : microphoneMode === 'CONTINUOUS');
|
|
826
|
-
setContinuousSpeechTimeout(getLocalConfig('continuousSpeechTimeout', 2));
|
|
827
|
-
setControlsPosition(
|
|
828
|
-
getLocalConfig('controlsPosition', defaultControlsPosition)
|
|
829
|
-
);
|
|
830
|
-
setAvatarType(getLocalConfig('avatarType', 'avatar3d'));
|
|
831
|
-
setHideEmissions(getLocalConfig('hideEmissions', false));
|
|
832
|
-
setRuntimeShowMessageConsumption(
|
|
833
|
-
getLocalConfig(
|
|
834
|
-
'showMessageConsumption',
|
|
835
|
-
showMessageConsumption ??
|
|
836
|
-
integrationConfig?.showMessageConsumption ??
|
|
837
|
-
false
|
|
838
|
-
)
|
|
839
|
-
);
|
|
840
|
-
|
|
841
|
-
if (!additionalInfo?.loginToken && !authToken) {
|
|
842
|
-
setLoginToken(getLocalConfig<typeof loginToken>('loginToken', undefined));
|
|
843
|
-
userTokenRef.current = getLocalConfig<typeof loginToken>('loginToken', undefined);
|
|
844
|
-
|
|
845
|
-
setBirthDate(getLocalConfig<string | undefined>('birthDate', undefined));
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
// If audio is disabled, automatically mute the speaker
|
|
849
|
-
if (!(enableAudio ?? integrationConfig?.enableAudio ?? true)) {
|
|
850
|
-
setLocalConfig('muteSpeaker', true);
|
|
851
|
-
}
|
|
852
|
-
}, []);
|
|
853
|
-
|
|
854
|
-
// Effect to handle enableAudio changes
|
|
855
|
-
useEffect(() => {
|
|
856
|
-
const isAudioEnabled =
|
|
857
|
-
enableAudio ?? integrationConfig?.enableAudio ?? true;
|
|
858
|
-
if (!isAudioEnabled) {
|
|
859
|
-
// Force mute when audio is disabled
|
|
860
|
-
setLocalConfig('muteSpeaker', true);
|
|
861
|
-
}
|
|
862
|
-
}, [enableAudio, integrationConfig?.enableAudio]);
|
|
863
|
-
|
|
864
|
-
/**
|
|
865
|
-
* Auth for private and secret memori
|
|
866
|
-
*/
|
|
867
|
-
const [memoriPwd, setMemoriPwd] = useState<string | undefined>(secret);
|
|
868
|
-
const [memoriTokens, setMemoriTokens] = useState<string[] | undefined>();
|
|
869
|
-
const [authModalState, setAuthModalState] = useState<
|
|
870
|
-
null | 'password' | 'tokens'
|
|
871
|
-
>(null);
|
|
872
|
-
|
|
873
|
-
/**
|
|
874
|
-
* Position drawer
|
|
875
|
-
*/
|
|
876
|
-
const [position, _setPosition] = useState<Venue>();
|
|
877
|
-
|
|
878
|
-
/** True when the user has set a real position; false when position is missing or "I don't want to provide my position". */
|
|
879
|
-
const hasUserProvidedPosition = useCallback((venue: Venue | undefined) => {
|
|
880
|
-
if (!venue) return false;
|
|
881
|
-
if (
|
|
882
|
-
venue.placeName === 'Position' &&
|
|
883
|
-
venue.latitude === 0 &&
|
|
884
|
-
venue.longitude === 0
|
|
885
|
-
) {
|
|
886
|
-
return false;
|
|
887
|
-
}
|
|
888
|
-
return true;
|
|
889
|
-
}, []);
|
|
890
|
-
|
|
891
|
-
/** Build optional place for EnterTextSpecs (placeName and/or lat/lon; lat/lon must be together). */
|
|
892
|
-
const buildEnterTextPlace = useCallback((venue: Venue | undefined) => {
|
|
893
|
-
if (!venue) return undefined;
|
|
894
|
-
const place: {
|
|
895
|
-
placeName?: string;
|
|
896
|
-
latitude?: number;
|
|
897
|
-
longitude?: number;
|
|
898
|
-
uncertaintyKm?: number;
|
|
899
|
-
} = {};
|
|
900
|
-
if (venue.latitude != null && venue.longitude != null) {
|
|
901
|
-
place.latitude = venue.latitude;
|
|
902
|
-
place.longitude = venue.longitude;
|
|
903
|
-
if (venue.placeName) place.placeName = venue.placeName;
|
|
904
|
-
if (venue.uncertainty != null && venue.uncertainty > 0)
|
|
905
|
-
place.uncertaintyKm = venue.uncertainty;
|
|
906
|
-
} else if (venue.placeName) {
|
|
907
|
-
place.placeName = venue.placeName;
|
|
908
|
-
}
|
|
909
|
-
return Object.keys(place).length > 0 ? place : undefined;
|
|
910
|
-
}, []);
|
|
911
|
-
|
|
912
|
-
/** Place to send with postEnterTextAsync: real place, nulls when no/declined position, or undefined when position not needed. */
|
|
913
|
-
const getPlaceSpecForEnterText = useCallback(
|
|
914
|
-
(venue: Venue | undefined) => {
|
|
915
|
-
if (!memori.needsPosition) return undefined;
|
|
916
|
-
return hasUserProvidedPosition(venue)
|
|
917
|
-
? buildEnterTextPlace(venue)
|
|
918
|
-
: NULL_PLACE_SPEC;
|
|
919
|
-
},
|
|
920
|
-
[memori.needsPosition, hasUserProvidedPosition, buildEnterTextPlace]
|
|
921
|
-
);
|
|
922
|
-
|
|
923
|
-
const setPosition = (venue?: Venue) => {
|
|
924
|
-
_setPosition(venue);
|
|
925
|
-
|
|
926
|
-
// Only save position to local config if memori.needsPosition is true
|
|
927
|
-
if (venue && memori.needsPosition) {
|
|
928
|
-
setLocalConfig('position', JSON.stringify(venue));
|
|
929
|
-
} else if (!venue) {
|
|
930
|
-
removeLocalConfig('position');
|
|
931
|
-
}
|
|
932
|
-
};
|
|
933
|
-
|
|
934
|
-
useEffect(() => {
|
|
935
|
-
// Only load position from local config if memori.needsPosition is true
|
|
936
|
-
if (memori.needsPosition) {
|
|
937
|
-
const position = getLocalConfig<Venue | undefined>('position', undefined);
|
|
938
|
-
if (position) {
|
|
939
|
-
_setPosition(position);
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
}, [memori.needsPosition]);
|
|
943
|
-
|
|
944
|
-
/**
|
|
945
|
-
* History e gestione invio messaggi
|
|
946
|
-
*/
|
|
947
|
-
const [userMessage, setUserMessage] = useState<string>('');
|
|
948
|
-
const onChangeUserMessage = (value: string) => {
|
|
949
|
-
if (!value || value === '\n' || value.trim() === '') {
|
|
950
|
-
setUserMessage('');
|
|
951
|
-
// resetInteractionTimeout();
|
|
952
|
-
return;
|
|
953
|
-
}
|
|
954
|
-
setUserMessage(value);
|
|
955
|
-
clearInteractionTimeout();
|
|
956
|
-
};
|
|
957
|
-
const [listening, setListening] = useState(false);
|
|
958
|
-
const [history, setHistory] = useState<Message[]>([]);
|
|
959
|
-
const pushMessage = (message: Message) => {
|
|
960
|
-
setHistory(history => [
|
|
961
|
-
...history,
|
|
962
|
-
{
|
|
963
|
-
...message,
|
|
964
|
-
media:
|
|
965
|
-
message.media?.filter(
|
|
966
|
-
m =>
|
|
967
|
-
!(m.mimeType === 'text/javascript' && !!m.properties?.executable)
|
|
968
|
-
) ?? [],
|
|
969
|
-
},
|
|
970
|
-
]);
|
|
971
|
-
};
|
|
972
|
-
|
|
973
|
-
// When a user resumes a chat, we need to set the chat reference link of the previous chat
|
|
974
|
-
const [chatLogID, setChatLogID] = useState<string | undefined>(undefined);
|
|
975
|
-
/**
|
|
976
|
-
* Sends a message to the Memori and handles the response
|
|
977
|
-
* @param text The text message to send
|
|
978
|
-
* @param media Optional media attachments
|
|
979
|
-
* @param newSessionId Optional new session ID to use
|
|
980
|
-
* @param translate Whether to translate the message before sending (default true)
|
|
981
|
-
* @param translatedText Optional pre-translated text
|
|
982
|
-
* @param hidden Whether to hide the message from chat history (default false)
|
|
983
|
-
* @param typingText Optional custom typing indicator text
|
|
984
|
-
* @param useLoaderTextAsMsg Whether to use the loader text as the message (default false)
|
|
985
|
-
* @param hasBatchQueued Whether there are more messages queued to be sent (default false)
|
|
986
|
-
* @param skipHistoryPush Skip adding the user message to history (e.g. session-expired retry)
|
|
987
|
-
*/
|
|
988
|
-
const sendMessage = async (
|
|
989
|
-
text: string,
|
|
990
|
-
media?: Medium[],
|
|
991
|
-
newSessionId?: string,
|
|
992
|
-
translate: boolean = true,
|
|
993
|
-
translatedText?: string,
|
|
994
|
-
hidden: boolean = false,
|
|
995
|
-
typingText?: string,
|
|
996
|
-
useLoaderTextAsMsg = false,
|
|
997
|
-
hasBatchQueued = false,
|
|
998
|
-
skipHistoryPush = false
|
|
999
|
-
) => {
|
|
1000
|
-
// Get the session ID from params or global state
|
|
1001
|
-
const sessionID =
|
|
1002
|
-
newSessionId ||
|
|
1003
|
-
sessionId ||
|
|
1004
|
-
(window.getMemoriState() as MemoriSession)?.sessionID;
|
|
1005
|
-
if (!sessionID || !text?.length) return;
|
|
1006
|
-
|
|
1007
|
-
// Build full message text (same as what will be sent) so we can run PII check on it.
|
|
1008
|
-
// Order: user text -> optional translation -> appended document attachment content.
|
|
1009
|
-
let msg = text;
|
|
1010
|
-
if (
|
|
1011
|
-
!hidden &&
|
|
1012
|
-
translate &&
|
|
1013
|
-
isMultilanguageEnabled &&
|
|
1014
|
-
userLang.toUpperCase() !== language.toUpperCase()
|
|
1015
|
-
) {
|
|
1016
|
-
const translation = await getTranslation(
|
|
1017
|
-
text,
|
|
1018
|
-
language,
|
|
1019
|
-
userLang,
|
|
1020
|
-
baseUrl
|
|
1021
|
-
);
|
|
1022
|
-
msg = translation.text;
|
|
1023
|
-
}
|
|
1024
|
-
const mediaDocuments = media?.filter(
|
|
1025
|
-
m => (m as any).type === 'document' && m.properties?.isAttachedFile
|
|
1026
|
-
);
|
|
1027
|
-
if (mediaDocuments && mediaDocuments.length > 0) {
|
|
1028
|
-
const documentContents = mediaDocuments.map(doc => doc.content).join(' ');
|
|
1029
|
-
msg = msg + ' ' + documentContents;
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
// PII check: when layout has piiDetection.enabled, run regex rules on the full msg.
|
|
1033
|
-
// If any rule matches, add the user message to history, then push the system error bubble and return without sending.
|
|
1034
|
-
if (piiDetection?.enabled) {
|
|
1035
|
-
const piiResult = checkPii(
|
|
1036
|
-
msg,
|
|
1037
|
-
piiDetection,
|
|
1038
|
-
userLang?.toLowerCase() || 'en'
|
|
1039
|
-
);
|
|
1040
|
-
if (piiResult.matched && piiResult.errorText) {
|
|
1041
|
-
if (!hidden) {
|
|
1042
|
-
pushMessage({
|
|
1043
|
-
text: text,
|
|
1044
|
-
translatedText,
|
|
1045
|
-
fromUser: true,
|
|
1046
|
-
media: media ?? [],
|
|
1047
|
-
initial: sessionId
|
|
1048
|
-
? !!newSessionId && newSessionId !== sessionId
|
|
1049
|
-
: !!newSessionId,
|
|
1050
|
-
});
|
|
1051
|
-
}
|
|
1052
|
-
pushMessage({
|
|
1053
|
-
text: piiResult.errorText,
|
|
1054
|
-
emitter: 'system',
|
|
1055
|
-
fromUser: false,
|
|
1056
|
-
initial: false,
|
|
1057
|
-
contextVars: {},
|
|
1058
|
-
date: new Date().toISOString(),
|
|
1059
|
-
});
|
|
1060
|
-
return;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
// Add user message to chat history if not hidden
|
|
1065
|
-
if (!hidden && !skipHistoryPush)
|
|
1066
|
-
pushMessage({
|
|
1067
|
-
text: text,
|
|
1068
|
-
translatedText,
|
|
1069
|
-
fromUser: true,
|
|
1070
|
-
media: media ?? [],
|
|
1071
|
-
initial: sessionId
|
|
1072
|
-
? !!newSessionId && newSessionId !== sessionId
|
|
1073
|
-
: !!newSessionId,
|
|
1074
|
-
});
|
|
1075
|
-
|
|
1076
|
-
// Show typing indicator after the async enter-text request is accepted (HTTP 200).
|
|
1077
|
-
let gotError = false;
|
|
1078
|
-
|
|
1079
|
-
try {
|
|
1080
|
-
const placeSpec = getPlaceSpecForEnterText(position);
|
|
1081
|
-
const response = await postEnterTextAsync({
|
|
1082
|
-
sessionId: sessionID,
|
|
1083
|
-
text: msg,
|
|
1084
|
-
...(memori.needsDateTime && {
|
|
1085
|
-
dateUTC: DateTime.utc().toISO() ?? undefined,
|
|
1086
|
-
}),
|
|
1087
|
-
...(placeSpec !== undefined && { place: placeSpec }),
|
|
1088
|
-
});
|
|
1089
|
-
const correlationID = readCorrelationID(response);
|
|
1090
|
-
if (response.resultCode === 0 && correlationID) {
|
|
1091
|
-
registerPendingEnterText(correlationID, {
|
|
1092
|
-
msg,
|
|
1093
|
-
stateBeforeRequest: dialogStateFingerprint(currentDialogState),
|
|
1094
|
-
text,
|
|
1095
|
-
media,
|
|
1096
|
-
translate,
|
|
1097
|
-
translatedText,
|
|
1098
|
-
hidden,
|
|
1099
|
-
typingText,
|
|
1100
|
-
useLoaderTextAsMsg,
|
|
1101
|
-
hasBatchQueued,
|
|
1102
|
-
});
|
|
1103
|
-
setMemoriTyping(true);
|
|
1104
|
-
setTypingText(typingText);
|
|
1105
|
-
} else if (response.resultCode === 0) {
|
|
1106
|
-
logWidgetError('enter-text missing correlationID', response);
|
|
1107
|
-
} else if (response.resultCode === 404) {
|
|
1108
|
-
retryAfterExpiredSessionRef.current({
|
|
1109
|
-
text,
|
|
1110
|
-
media,
|
|
1111
|
-
translate,
|
|
1112
|
-
translatedText,
|
|
1113
|
-
hidden,
|
|
1114
|
-
typingText,
|
|
1115
|
-
useLoaderTextAsMsg,
|
|
1116
|
-
hasBatchQueued,
|
|
1117
|
-
expiredSessionID: sessionID,
|
|
1118
|
-
continueFromChatLogID: chatLogID,
|
|
1119
|
-
});
|
|
1120
|
-
} else if (response.resultCode === 500 && response.resultMessage) {
|
|
1121
|
-
setHistory(h => [
|
|
1122
|
-
...h,
|
|
1123
|
-
{
|
|
1124
|
-
text: 'Error: ' + response.resultMessage,
|
|
1125
|
-
emitter: 'system',
|
|
1126
|
-
fromUser: false,
|
|
1127
|
-
initial: false,
|
|
1128
|
-
contextVars: {},
|
|
1129
|
-
date: new Date().toISOString(),
|
|
1130
|
-
},
|
|
1131
|
-
]);
|
|
1132
|
-
} else {
|
|
1133
|
-
return Promise.reject(response);
|
|
1134
|
-
}
|
|
1135
|
-
} catch (error) {
|
|
1136
|
-
gotError = true;
|
|
1137
|
-
logWidgetError('sendMessage failed', error);
|
|
1138
|
-
|
|
1139
|
-
setTypingText(undefined);
|
|
1140
|
-
setMemoriTyping(false);
|
|
1141
|
-
}
|
|
1142
|
-
};
|
|
1143
|
-
|
|
1144
|
-
/**
|
|
1145
|
-
* An enhanced version of translateDialogState that integrates smooth speaking
|
|
1146
|
-
* This preserves all your existing logic while improving speech reliability
|
|
1147
|
-
*/
|
|
1148
|
-
const translateDialogState = async (
|
|
1149
|
-
state: DialogState,
|
|
1150
|
-
userLang: string,
|
|
1151
|
-
msg?: string,
|
|
1152
|
-
avoidPushingMessage: boolean = false
|
|
1153
|
-
) => {
|
|
1154
|
-
const emission = state?.emission ?? currentDialogState?.emission;
|
|
1155
|
-
|
|
1156
|
-
let translatedState = { ...state };
|
|
1157
|
-
let translatedMsg: any = null;
|
|
1158
|
-
|
|
1159
|
-
// Skip translation if not needed
|
|
1160
|
-
if (
|
|
1161
|
-
!emission ||
|
|
1162
|
-
language.toUpperCase() === userLang.toUpperCase() ||
|
|
1163
|
-
!isMultilanguageEnabled ||
|
|
1164
|
-
avoidPushingMessage
|
|
1165
|
-
) {
|
|
1166
|
-
translatedState = { ...state, emission };
|
|
1167
|
-
if (emission) {
|
|
1168
|
-
translatedMsg = {
|
|
1169
|
-
text: emission,
|
|
1170
|
-
emitter: state.emitter,
|
|
1171
|
-
media: state.emittedMedia ?? state.media,
|
|
1172
|
-
llmUsage: (state as any).llmUsage,
|
|
1173
|
-
fromUser: false,
|
|
1174
|
-
questionAnswered: msg,
|
|
1175
|
-
contextVars: state.contextVars,
|
|
1176
|
-
date: state.currentDate,
|
|
1177
|
-
placeName: state.currentPlaceName,
|
|
1178
|
-
placeLatitude: state.currentLatitude,
|
|
1179
|
-
placeLongitude: state.currentLongitude,
|
|
1180
|
-
placeUncertaintyKm: state.currentUncertaintyKm,
|
|
1181
|
-
tag: state.currentTag,
|
|
1182
|
-
memoryTags: state.memoryTags,
|
|
1183
|
-
};
|
|
1184
|
-
}
|
|
1185
|
-
} else {
|
|
1186
|
-
try {
|
|
1187
|
-
const t = await getTranslation(emission, userLang, language, baseUrl);
|
|
1188
|
-
|
|
1189
|
-
// Handle hints translation if present
|
|
1190
|
-
if (state.hints && state.hints.length > 0) {
|
|
1191
|
-
const translatedHints = await Promise.all(
|
|
1192
|
-
(state.hints ?? []).map(async hint => {
|
|
1193
|
-
const tHint = await getTranslation(
|
|
1194
|
-
hint,
|
|
1195
|
-
userLang,
|
|
1196
|
-
language,
|
|
1197
|
-
baseUrl
|
|
1198
|
-
);
|
|
1199
|
-
return {
|
|
1200
|
-
text: tHint?.text ?? hint,
|
|
1201
|
-
originalText: hint,
|
|
1202
|
-
} as TranslatedHint;
|
|
1203
|
-
})
|
|
1204
|
-
);
|
|
1205
|
-
translatedState = {
|
|
1206
|
-
...state,
|
|
1207
|
-
emission: t.text,
|
|
1208
|
-
translatedHints,
|
|
1209
|
-
};
|
|
1210
|
-
} else {
|
|
1211
|
-
translatedState = {
|
|
1212
|
-
...state,
|
|
1213
|
-
emission: emission,
|
|
1214
|
-
translatedEmission: t.text,
|
|
1215
|
-
hints:
|
|
1216
|
-
state.hints ??
|
|
1217
|
-
(state.state === 'G1' ? currentDialogState?.hints : []),
|
|
1218
|
-
};
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
if (t.text.length > 0) {
|
|
1222
|
-
translatedMsg = {
|
|
1223
|
-
text: emission,
|
|
1224
|
-
translatedText: t.text,
|
|
1225
|
-
emitter: state.emitter,
|
|
1226
|
-
media: state.emittedMedia ?? state.media,
|
|
1227
|
-
llmUsage: (state as any).llmUsage,
|
|
1228
|
-
fromUser: false,
|
|
1229
|
-
questionAnswered: msg,
|
|
1230
|
-
generatedByAI: !!state.completion,
|
|
1231
|
-
contextVars: state.contextVars,
|
|
1232
|
-
date: state.currentDate,
|
|
1233
|
-
placeName: state.currentPlaceName,
|
|
1234
|
-
placeLatitude: state.currentLatitude,
|
|
1235
|
-
placeLongitude: state.currentLongitude,
|
|
1236
|
-
placeUncertaintyKm: state.currentUncertaintyKm,
|
|
1237
|
-
tag: state.currentTag,
|
|
1238
|
-
memoryTags: state.memoryTags,
|
|
1239
|
-
};
|
|
1240
|
-
}
|
|
1241
|
-
} catch (error) {
|
|
1242
|
-
translatedState = { ...state, emission };
|
|
1243
|
-
translatedMsg = {
|
|
1244
|
-
text: emission,
|
|
1245
|
-
emitter: state.emitter,
|
|
1246
|
-
media: state.emittedMedia ?? state.media,
|
|
1247
|
-
llmUsage: (state as any).llmUsage,
|
|
1248
|
-
fromUser: false,
|
|
1249
|
-
questionAnswered: msg,
|
|
1250
|
-
contextVars: state.contextVars,
|
|
1251
|
-
date: state.currentDate,
|
|
1252
|
-
placeName: state.currentPlaceName,
|
|
1253
|
-
placeLatitude: state.currentLatitude,
|
|
1254
|
-
placeLongitude: state.currentLongitude,
|
|
1255
|
-
placeUncertaintyKm: state.currentUncertaintyKm,
|
|
1256
|
-
tag: state.currentTag,
|
|
1257
|
-
memoryTags: state.memoryTags,
|
|
1258
|
-
};
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
|
|
1262
|
-
setCurrentDialogState(translatedState);
|
|
1263
|
-
if (!avoidPushingMessage && translatedMsg) {
|
|
1264
|
-
pushMessage(translatedMsg);
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
return translatedState;
|
|
1268
|
-
};
|
|
1269
|
-
|
|
1270
|
-
/**
|
|
1271
|
-
* Age verification
|
|
1272
|
-
*/
|
|
1273
|
-
const minAge =
|
|
1274
|
-
memori.ageRestriction !== undefined
|
|
1275
|
-
? memori.ageRestriction
|
|
1276
|
-
: memori.nsfw
|
|
1277
|
-
? 18
|
|
1278
|
-
: memori.enableCompletions
|
|
1279
|
-
? 14
|
|
1280
|
-
: 0;
|
|
1281
|
-
const [birthDate, setBirthDate] = useState<string | undefined>();
|
|
1282
|
-
const [showAgeVerification, setShowAgeVerification] = useState(false);
|
|
1283
|
-
|
|
1284
|
-
const getCultureCodeByLanguage = (lang?: string): string => {
|
|
1285
|
-
let voice = '';
|
|
1286
|
-
let voiceLang = (
|
|
1287
|
-
lang ||
|
|
1288
|
-
memori.culture?.split('-')?.[0] ||
|
|
1289
|
-
i18n.language ||
|
|
1290
|
-
'IT'
|
|
1291
|
-
).toUpperCase();
|
|
1292
|
-
switch (voiceLang) {
|
|
1293
|
-
case 'IT':
|
|
1294
|
-
voice = 'it-IT';
|
|
1295
|
-
break;
|
|
1296
|
-
case 'DE':
|
|
1297
|
-
voice = 'de-DE';
|
|
1298
|
-
break;
|
|
1299
|
-
case 'EN':
|
|
1300
|
-
voice = 'en-GB';
|
|
1301
|
-
break;
|
|
1302
|
-
case 'ES':
|
|
1303
|
-
voice = 'es-ES';
|
|
1304
|
-
break;
|
|
1305
|
-
case 'FR':
|
|
1306
|
-
voice = 'fr-FR';
|
|
1307
|
-
break;
|
|
1308
|
-
case 'PT':
|
|
1309
|
-
voice = 'pt-PT';
|
|
1310
|
-
break;
|
|
1311
|
-
case 'UK':
|
|
1312
|
-
voice = 'uk-UK';
|
|
1313
|
-
break;
|
|
1314
|
-
case 'RU':
|
|
1315
|
-
voice = 'ru-RU';
|
|
1316
|
-
break;
|
|
1317
|
-
case 'PL':
|
|
1318
|
-
voice = 'pl-PL';
|
|
1319
|
-
break;
|
|
1320
|
-
case 'FI':
|
|
1321
|
-
voice = 'fi-FI';
|
|
1322
|
-
break;
|
|
1323
|
-
case 'EL':
|
|
1324
|
-
voice = 'el-GR';
|
|
1325
|
-
break;
|
|
1326
|
-
case 'AR':
|
|
1327
|
-
voice = 'ar-SA';
|
|
1328
|
-
break;
|
|
1329
|
-
case 'ZH':
|
|
1330
|
-
voice = 'zh-CN';
|
|
1331
|
-
break;
|
|
1332
|
-
case 'JA':
|
|
1333
|
-
voice = 'ja-JP';
|
|
1334
|
-
break;
|
|
1335
|
-
default:
|
|
1336
|
-
voice = 'it-IT';
|
|
1337
|
-
break;
|
|
1338
|
-
}
|
|
1339
|
-
return voice;
|
|
1340
|
-
};
|
|
1341
|
-
|
|
1342
|
-
/**
|
|
1343
|
-
* Sessione
|
|
1344
|
-
*/
|
|
1345
|
-
const [sessionId, setSessionId] = useState<string | undefined>(
|
|
1346
|
-
initialSessionID
|
|
1347
|
-
);
|
|
1348
|
-
const [currentDialogState, _setCurrentDialogState] = useState<DialogState>();
|
|
1349
|
-
const setCurrentDialogState = (state?: DialogState) => {
|
|
1350
|
-
_setCurrentDialogState(state);
|
|
1351
|
-
if (onStateChange) {
|
|
1352
|
-
onStateChange(state);
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
const e: MemoriNewDialogStateEvent = new CustomEvent(
|
|
1356
|
-
'MemoriNewDialogState',
|
|
1357
|
-
{
|
|
1358
|
-
detail: state,
|
|
1359
|
-
}
|
|
1360
|
-
);
|
|
1361
|
-
document.dispatchEvent(e);
|
|
1362
|
-
|
|
1363
|
-
const executableSnippets = (state?.emittedMedia ?? state?.media)?.filter(
|
|
1364
|
-
m => m.mimeType === 'text/javascript' && !!m.properties?.executable
|
|
1365
|
-
);
|
|
1366
|
-
executableSnippets?.forEach(s => {
|
|
1367
|
-
try {
|
|
1368
|
-
setTimeout(() => {
|
|
1369
|
-
// eslint-disable-next-line no-new-func
|
|
1370
|
-
new Function(s.content ?? '')();
|
|
1371
|
-
|
|
1372
|
-
setTimeout(() => {
|
|
1373
|
-
document
|
|
1374
|
-
.querySelector('.memori-chat--content')
|
|
1375
|
-
?.scrollTo(
|
|
1376
|
-
0,
|
|
1377
|
-
document.querySelector('.memori-chat--content')?.scrollHeight ??
|
|
1378
|
-
0
|
|
1379
|
-
);
|
|
1380
|
-
}, 400);
|
|
1381
|
-
}, 1000);
|
|
1382
|
-
} catch {
|
|
1383
|
-
// ignore snippet execution errors
|
|
1384
|
-
}
|
|
1385
|
-
});
|
|
1386
|
-
};
|
|
1387
|
-
|
|
1388
|
-
useEffect(() => {
|
|
1389
|
-
if (initialSessionID) {
|
|
1390
|
-
setSessionId(initialSessionID);
|
|
1391
|
-
onClickStart(undefined, false, undefined, initialSessionID);
|
|
1392
|
-
}
|
|
1393
|
-
}, [initialSessionID]);
|
|
1394
|
-
|
|
1395
|
-
/**
|
|
1396
|
-
* Opening Session
|
|
1397
|
-
*/
|
|
1398
|
-
/**
|
|
1399
|
-
* Fetches a new session with the given parameters
|
|
1400
|
-
* @param params OpenSession parameters
|
|
1401
|
-
* @returns Promise resolving to dialog state and session ID if successful, void otherwise
|
|
1402
|
-
*/
|
|
1403
|
-
const fetchSession = async (
|
|
1404
|
-
params: OpenSession
|
|
1405
|
-
): Promise<
|
|
1406
|
-
| (ResponseSpec & {
|
|
1407
|
-
dialogState?: DialogState;
|
|
1408
|
-
sessionID: string;
|
|
1409
|
-
})
|
|
1410
|
-
| undefined
|
|
1411
|
-
| void
|
|
1412
|
-
> => {
|
|
1413
|
-
let storageBirthDate = getLocalConfig<string | undefined>(
|
|
1414
|
-
'birthDate',
|
|
1415
|
-
undefined
|
|
1416
|
-
);
|
|
1417
|
-
let userBirthDate = birthDate ?? params.birthDate ?? storageBirthDate;
|
|
1418
|
-
if (!userBirthDate && !!minAge) {
|
|
1419
|
-
setShowAgeVerification(true);
|
|
1420
|
-
return;
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
// Check if authentication is needed for private Memori
|
|
1424
|
-
if (
|
|
1425
|
-
memori.privacyType !== 'PUBLIC' &&
|
|
1426
|
-
!memori.secretToken &&
|
|
1427
|
-
!memoriPwd &&
|
|
1428
|
-
!memoriTokens
|
|
1429
|
-
) {
|
|
1430
|
-
setAuthModalState('password');
|
|
1431
|
-
return;
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
if (!(await checkCredits({ notify: true }))) {
|
|
1435
|
-
return;
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1438
|
-
setLoading(true);
|
|
1439
|
-
|
|
1440
|
-
try {
|
|
1441
|
-
// Check for and set giver invitation if available
|
|
1442
|
-
// if (!memori.giverTag && !!memori.receivedInvitations?.length) {
|
|
1443
|
-
// let giverInvitation = memori.receivedInvitations.find(
|
|
1444
|
-
// (i: Invitation) => i.type === 'GIVER' && i.state === 'ACCEPTED'
|
|
1445
|
-
// );
|
|
1446
|
-
|
|
1447
|
-
// if (giverInvitation) {x
|
|
1448
|
-
// memori.giverTag = giverInvitation.tag;
|
|
1449
|
-
// memori.giverPIN = giverInvitation.pin;
|
|
1450
|
-
// }
|
|
1451
|
-
// }
|
|
1452
|
-
|
|
1453
|
-
// Get referral URL
|
|
1454
|
-
let referral;
|
|
1455
|
-
try {
|
|
1456
|
-
referral = (() => {
|
|
1457
|
-
return window.location.href;
|
|
1458
|
-
})();
|
|
1459
|
-
} catch {
|
|
1460
|
-
// ignore referral URL errors
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
// Initialize session with parameters
|
|
1464
|
-
const session = await initSession({
|
|
1465
|
-
...params,
|
|
1466
|
-
birthDate: userBirthDate,
|
|
1467
|
-
tag: params.tag ?? personification?.tag,
|
|
1468
|
-
pin: params.pin ?? personification?.pin,
|
|
1469
|
-
additionalInfo: {
|
|
1470
|
-
...(params.additionalInfo || additionalInfo || {}),
|
|
1471
|
-
loginToken: resolveLoginToken(params.additionalInfo?.loginToken),
|
|
1472
|
-
language: (
|
|
1473
|
-
userLang ??
|
|
1474
|
-
memori.culture?.split('-')?.[0] ??
|
|
1475
|
-
'IT'
|
|
1476
|
-
).toLowerCase(),
|
|
1477
|
-
referral: referral,
|
|
1478
|
-
timeZoneOffset: new Date().getTimezoneOffset().toString(),
|
|
1479
|
-
},
|
|
1480
|
-
});
|
|
1481
|
-
|
|
1482
|
-
// Handle successful session creation
|
|
1483
|
-
if (
|
|
1484
|
-
session?.sessionID &&
|
|
1485
|
-
session?.currentState &&
|
|
1486
|
-
session.resultCode === 0
|
|
1487
|
-
) {
|
|
1488
|
-
setSessionId(session.sessionID);
|
|
1489
|
-
|
|
1490
|
-
// save giver state
|
|
1491
|
-
if (currentDialogState?.currentTag && memori.giverTag) {
|
|
1492
|
-
setInstruct(currentDialogState?.currentTag === memori.giverTag);
|
|
1493
|
-
} else {
|
|
1494
|
-
setInstruct(false);
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
setLoading(false);
|
|
1498
|
-
return {
|
|
1499
|
-
dialogState: session.currentState,
|
|
1500
|
-
sessionID: session.sessionID,
|
|
1501
|
-
} as any;
|
|
1502
|
-
}
|
|
1503
|
-
// Handle age restriction error
|
|
1504
|
-
else if (
|
|
1505
|
-
session?.resultMessage.startsWith('This Memori is aged restricted')
|
|
1506
|
-
) {
|
|
1507
|
-
toast.error(t('underageTwinSession', { age: minAge }));
|
|
1508
|
-
}
|
|
1509
|
-
// Handle authentication error
|
|
1510
|
-
else if (session?.resultCode === 403 && memori.privacyType !== 'PUBLIC') {
|
|
1511
|
-
setMemoriPwd(undefined);
|
|
1512
|
-
setAuthModalState('password');
|
|
1513
|
-
return session;
|
|
1514
|
-
}
|
|
1515
|
-
// Handle other errors
|
|
1516
|
-
else {
|
|
1517
|
-
toast.error(
|
|
1518
|
-
tst => (
|
|
1519
|
-
<div>
|
|
1520
|
-
<p>{t(getErrori18nKey(session?.resultCode))}</p>
|
|
1521
|
-
<Button
|
|
1522
|
-
outlined
|
|
1523
|
-
padded={false}
|
|
1524
|
-
onClick={() => toast.dismiss(tst.id)}
|
|
1525
|
-
icon={<CloseIcon />}
|
|
1526
|
-
>
|
|
1527
|
-
{t('close')}
|
|
1528
|
-
</Button>
|
|
1529
|
-
</div>
|
|
1530
|
-
),
|
|
1531
|
-
{
|
|
1532
|
-
duration: Infinity,
|
|
1533
|
-
}
|
|
1534
|
-
);
|
|
1535
|
-
return session;
|
|
1536
|
-
}
|
|
1537
|
-
} catch (err) {
|
|
1538
|
-
logWidgetError('fetchSession failed', err);
|
|
1539
|
-
}
|
|
1540
|
-
};
|
|
1541
|
-
|
|
1542
|
-
/**
|
|
1543
|
-
* Reopens an existing session with optional parameters
|
|
1544
|
-
* @param updateDialogState Whether to update dialog state
|
|
1545
|
-
* @param password Optional password for authentication
|
|
1546
|
-
* @param recoveryTokens Optional recovery tokens
|
|
1547
|
-
* @param tag Optional tag
|
|
1548
|
-
* @param pin Optional PIN
|
|
1549
|
-
* @param initialContextVars Optional initial context variables
|
|
1550
|
-
* @param initialQuestion Optional initial question
|
|
1551
|
-
* @param birthDate Optional birth date for age verification
|
|
1552
|
-
* @returns Promise resolving to dialog state and session ID if successful, null otherwise
|
|
1553
|
-
*/
|
|
1554
|
-
const reopenSession = async (
|
|
1555
|
-
updateDialogState: boolean = false,
|
|
1556
|
-
password?: string,
|
|
1557
|
-
recoveryTokens?: string[],
|
|
1558
|
-
tag?: string,
|
|
1559
|
-
pin?: string,
|
|
1560
|
-
initialContextVars?: { [key: string]: string },
|
|
1561
|
-
initialQuestion?: string,
|
|
1562
|
-
birthDate?: string,
|
|
1563
|
-
additionalInfoProp?: { [key: string]: string | undefined },
|
|
1564
|
-
continueFromChatLogID?: string,
|
|
1565
|
-
continueFromSessionID?: string,
|
|
1566
|
-
isSessionExpired?: boolean,
|
|
1567
|
-
suppressHistoryUpdate?: boolean
|
|
1568
|
-
) => {
|
|
1569
|
-
// Set loading state while reopening session
|
|
1570
|
-
setLoading(true);
|
|
1571
|
-
|
|
1572
|
-
// Get birth date from local storage if not provided
|
|
1573
|
-
let storageBirthDate = getLocalConfig<string | undefined>(
|
|
1574
|
-
'birthDate',
|
|
1575
|
-
undefined
|
|
1576
|
-
);
|
|
1577
|
-
let userBirthDate = birthDate ?? storageBirthDate;
|
|
1578
|
-
|
|
1579
|
-
try {
|
|
1580
|
-
// Show age verification if required and birth date not provided
|
|
1581
|
-
if (!userBirthDate && !!minAge) {
|
|
1582
|
-
setShowAgeVerification(true);
|
|
1583
|
-
return;
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
// Check if authentication is needed based on privacy type and credentials
|
|
1587
|
-
if (
|
|
1588
|
-
memori.privacyType !== 'PUBLIC' &&
|
|
1589
|
-
!password &&
|
|
1590
|
-
!memori.secretToken &&
|
|
1591
|
-
!memoriPwd &&
|
|
1592
|
-
!recoveryTokens &&
|
|
1593
|
-
!memoriTokens
|
|
1594
|
-
) {
|
|
1595
|
-
setAuthModalState('password');
|
|
1596
|
-
return;
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
|
-
if (!(await checkCredits({ notify: true }))) {
|
|
1600
|
-
setLoading(false);
|
|
1601
|
-
return null;
|
|
1602
|
-
}
|
|
1603
|
-
|
|
1604
|
-
// Get current URL as referral
|
|
1605
|
-
let referral;
|
|
1606
|
-
try {
|
|
1607
|
-
referral = (() => {
|
|
1608
|
-
return window.location.href;
|
|
1609
|
-
})();
|
|
1610
|
-
} catch {
|
|
1611
|
-
// ignore referral URL errors
|
|
1612
|
-
}
|
|
1613
|
-
|
|
1614
|
-
// Initialize session with provided parameters
|
|
1615
|
-
const { sessionID, currentState, ...response } = await initSession({
|
|
1616
|
-
memoriID: memori.engineMemoriID ?? '',
|
|
1617
|
-
password: password || memoriPwd || memori.secretToken,
|
|
1618
|
-
recoveryTokens: recoveryTokens || memoriTokens,
|
|
1619
|
-
tag: tag ?? personification?.tag,
|
|
1620
|
-
pin: pin ?? personification?.pin,
|
|
1621
|
-
continueFromChatLogID: continueFromChatLogID,
|
|
1622
|
-
continueFromSessionID: continueFromSessionID,
|
|
1623
|
-
initialContextVars: {
|
|
1624
|
-
LANG: userLang,
|
|
1625
|
-
PATHNAME: window.location.pathname,
|
|
1626
|
-
ROUTE: window.location.pathname?.split('/')?.pop() || '',
|
|
1627
|
-
...(initialContextVars || {}),
|
|
1628
|
-
},
|
|
1629
|
-
initialQuestion,
|
|
1630
|
-
birthDate: userBirthDate,
|
|
1631
|
-
additionalInfo: {
|
|
1632
|
-
...(additionalInfoProp || additionalInfo || {}),
|
|
1633
|
-
loginToken: resolveLoginToken(additionalInfoProp?.loginToken),
|
|
1634
|
-
language: (
|
|
1635
|
-
userLang ??
|
|
1636
|
-
memori.culture?.split('-')?.[0] ??
|
|
1637
|
-
'IT'
|
|
1638
|
-
).toLowerCase(),
|
|
1639
|
-
referral: referral,
|
|
1640
|
-
timeZoneOffset: new Date().getTimezoneOffset().toString(),
|
|
1641
|
-
},
|
|
1642
|
-
});
|
|
1643
|
-
|
|
1644
|
-
// Handle successful session initialization
|
|
1645
|
-
if (sessionID && currentState && response.resultCode === 0) {
|
|
1646
|
-
setSessionId(sessionID);
|
|
1647
|
-
|
|
1648
|
-
// Update dialog state and history if requested
|
|
1649
|
-
if (updateDialogState) {
|
|
1650
|
-
setCurrentDialogState(currentState);
|
|
1651
|
-
|
|
1652
|
-
const sessionExpiredStatus =
|
|
1653
|
-
isSessionExpired && history.length > 1
|
|
1654
|
-
? t('sessionExpiredReopening')
|
|
1655
|
-
: null;
|
|
1656
|
-
|
|
1657
|
-
if (sessionExpiredStatus && suppressHistoryUpdate) {
|
|
1658
|
-
pushMessage({
|
|
1659
|
-
text: '',
|
|
1660
|
-
emitter: 'system',
|
|
1661
|
-
fromUser: false,
|
|
1662
|
-
initial: sessionExpiredStatus as any,
|
|
1663
|
-
contextVars: {},
|
|
1664
|
-
date: new Date().toISOString(),
|
|
1665
|
-
});
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
if (currentState.emission && !suppressHistoryUpdate) {
|
|
1669
|
-
// Determine initial status message based on context
|
|
1670
|
-
// Show status message only if session expired and there's existing history
|
|
1671
|
-
const initialStatus =
|
|
1672
|
-
sessionExpiredStatus
|
|
1673
|
-
? sessionExpiredStatus
|
|
1674
|
-
: history.length <= 1
|
|
1675
|
-
? true
|
|
1676
|
-
: undefined;
|
|
1677
|
-
|
|
1678
|
-
// Set initial message or append to existing history
|
|
1679
|
-
history.length <= 1
|
|
1680
|
-
? setHistory([
|
|
1681
|
-
{
|
|
1682
|
-
text: currentState.emission,
|
|
1683
|
-
emitter: currentState.emitter,
|
|
1684
|
-
media: currentState.emittedMedia ?? currentState.media,
|
|
1685
|
-
fromUser: false,
|
|
1686
|
-
initial: (initialStatus === true
|
|
1687
|
-
? true
|
|
1688
|
-
: initialStatus || undefined) as any,
|
|
1689
|
-
contextVars: currentState.contextVars,
|
|
1690
|
-
date: currentState.currentDate,
|
|
1691
|
-
placeName: currentState.currentPlaceName,
|
|
1692
|
-
placeLatitude: currentState.currentLatitude,
|
|
1693
|
-
placeLongitude: currentState.currentLongitude,
|
|
1694
|
-
placeUncertaintyKm: currentState.currentUncertaintyKm,
|
|
1695
|
-
tag: currentState.currentTag,
|
|
1696
|
-
memoryTags: currentState.memoryTags,
|
|
1697
|
-
},
|
|
1698
|
-
])
|
|
1699
|
-
: pushMessage({
|
|
1700
|
-
text: currentState.emission,
|
|
1701
|
-
emitter: currentState.emitter,
|
|
1702
|
-
media: currentState.emittedMedia ?? currentState.media,
|
|
1703
|
-
fromUser: false,
|
|
1704
|
-
initial: (initialStatus === true
|
|
1705
|
-
? true
|
|
1706
|
-
: initialStatus || undefined) as any,
|
|
1707
|
-
contextVars: currentState.contextVars,
|
|
1708
|
-
date: currentState.currentDate,
|
|
1709
|
-
placeName: currentState.currentPlaceName,
|
|
1710
|
-
placeLatitude: currentState.currentLatitude,
|
|
1711
|
-
placeLongitude: currentState.currentLongitude,
|
|
1712
|
-
placeUncertaintyKm: currentState.currentUncertaintyKm,
|
|
1713
|
-
tag: currentState.currentTag,
|
|
1714
|
-
memoryTags: currentState.memoryTags,
|
|
1715
|
-
});
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
|
-
setLoading(false);
|
|
1720
|
-
return {
|
|
1721
|
-
dialogState: currentState,
|
|
1722
|
-
sessionID,
|
|
1723
|
-
};
|
|
1724
|
-
}
|
|
1725
|
-
// Handle age restriction error
|
|
1726
|
-
else if (
|
|
1727
|
-
response?.resultMessage.startsWith('This Memori is aged restricted')
|
|
1728
|
-
) {
|
|
1729
|
-
toast.error(t('underageTwinSession', { age: minAge }));
|
|
1730
|
-
}
|
|
1731
|
-
// Handle authentication error
|
|
1732
|
-
else if (
|
|
1733
|
-
response?.resultCode === 403 &&
|
|
1734
|
-
memori.privacyType !== 'PUBLIC'
|
|
1735
|
-
) {
|
|
1736
|
-
setMemoriPwd(undefined);
|
|
1737
|
-
setAuthModalState('password');
|
|
1738
|
-
}
|
|
1739
|
-
// Handle other errors
|
|
1740
|
-
else {
|
|
1741
|
-
toast.error(t(getErrori18nKey(response.resultCode)));
|
|
1742
|
-
}
|
|
1743
|
-
} catch (err) {
|
|
1744
|
-
logWidgetError('reopenSession failed', err);
|
|
1745
|
-
}
|
|
1746
|
-
// Reset loading state
|
|
1747
|
-
setLoading(false);
|
|
1748
|
-
|
|
1749
|
-
return null;
|
|
1750
|
-
};
|
|
1751
|
-
|
|
1752
|
-
const retryAfterExpiredSessionRef = useRef<
|
|
1753
|
-
(params: EnterTextRetryParams) => ReturnType<typeof reopenSession>
|
|
1754
|
-
>(() => Promise.resolve(null));
|
|
1755
|
-
|
|
1756
|
-
retryAfterExpiredSessionRef.current = (params: EnterTextRetryParams) => {
|
|
1757
|
-
const {
|
|
1758
|
-
text,
|
|
1759
|
-
media,
|
|
1760
|
-
translate = true,
|
|
1761
|
-
translatedText,
|
|
1762
|
-
hidden = false,
|
|
1763
|
-
typingText,
|
|
1764
|
-
useLoaderTextAsMsg = false,
|
|
1765
|
-
hasBatchQueued = false,
|
|
1766
|
-
expiredSessionID,
|
|
1767
|
-
continueFromChatLogID: continueFromChatLogIDParam,
|
|
1768
|
-
} = params;
|
|
1769
|
-
|
|
1770
|
-
const continueFromSessionID = expiredSessionID ?? sessionId;
|
|
1771
|
-
const continueFromChatLogID = continueFromChatLogIDParam ?? chatLogID;
|
|
1772
|
-
|
|
1773
|
-
const reopenAfterExpiry = () =>
|
|
1774
|
-
reopenSession(
|
|
1775
|
-
true,
|
|
1776
|
-
memoriPwd || memori.secretToken,
|
|
1777
|
-
memoriTokens,
|
|
1778
|
-
undefined,
|
|
1779
|
-
undefined,
|
|
1780
|
-
{
|
|
1781
|
-
LANG: userLang,
|
|
1782
|
-
PATHNAME: window.location.pathname,
|
|
1783
|
-
ROUTE: window.location.pathname?.split('/')?.pop() || '',
|
|
1784
|
-
...(initialContextVars || {}),
|
|
1785
|
-
},
|
|
1786
|
-
initialQuestion,
|
|
1787
|
-
undefined,
|
|
1788
|
-
undefined,
|
|
1789
|
-
continueFromChatLogID,
|
|
1790
|
-
continueFromSessionID,
|
|
1791
|
-
true,
|
|
1792
|
-
true
|
|
1793
|
-
);
|
|
1794
|
-
|
|
1795
|
-
const scheduleRetry = (newSessionID: string) => {
|
|
1796
|
-
setTimeout(() => {
|
|
1797
|
-
sendMessage(
|
|
1798
|
-
text!,
|
|
1799
|
-
media,
|
|
1800
|
-
newSessionID,
|
|
1801
|
-
translate,
|
|
1802
|
-
translatedText,
|
|
1803
|
-
hidden,
|
|
1804
|
-
typingText,
|
|
1805
|
-
useLoaderTextAsMsg,
|
|
1806
|
-
hasBatchQueued,
|
|
1807
|
-
true
|
|
1808
|
-
);
|
|
1809
|
-
}, 500);
|
|
1810
|
-
};
|
|
1811
|
-
|
|
1812
|
-
const handleReopenFailure = (state: Awaited<ReturnType<typeof reopenSession>>) => {
|
|
1813
|
-
setMemoriTyping(false);
|
|
1814
|
-
setTypingText(undefined);
|
|
1815
|
-
// `null` = explicit failure; `undefined` = auth/age modal opened (message kept in history)
|
|
1816
|
-
if (state === null && text && !hidden) {
|
|
1817
|
-
toast.error(t('errors.SESSION_EXPIRED'));
|
|
1818
|
-
}
|
|
1819
|
-
};
|
|
1820
|
-
|
|
1821
|
-
if (!text) {
|
|
1822
|
-
return reopenAfterExpiry().then(state => {
|
|
1823
|
-
if (!state?.sessionID) {
|
|
1824
|
-
handleReopenFailure(state);
|
|
1825
|
-
}
|
|
1826
|
-
return state;
|
|
1827
|
-
});
|
|
1828
|
-
}
|
|
1829
|
-
|
|
1830
|
-
return reopenAfterExpiry().then(state => {
|
|
1831
|
-
if (state?.sessionID) {
|
|
1832
|
-
scheduleRetry(state.sessionID);
|
|
1833
|
-
} else {
|
|
1834
|
-
handleReopenFailure(state);
|
|
1835
|
-
}
|
|
1836
|
-
return state;
|
|
1837
|
-
});
|
|
1838
|
-
};
|
|
1839
|
-
|
|
1840
|
-
const changeTag = async (
|
|
1841
|
-
memoriId: string,
|
|
1842
|
-
sessionId: string,
|
|
1843
|
-
tag?: string,
|
|
1844
|
-
pin?: string
|
|
1845
|
-
) => {
|
|
1846
|
-
if (!memoriId || !sessionId) {
|
|
1847
|
-
return Promise.reject('Session not found');
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
try {
|
|
1851
|
-
const { currentState, resultCode } = await postTagChangedEvent(
|
|
1852
|
-
sessionId,
|
|
1853
|
-
tag ?? anonTag
|
|
1854
|
-
);
|
|
1855
|
-
|
|
1856
|
-
if (resultCode === 0) {
|
|
1857
|
-
let textResult = 0;
|
|
1858
|
-
if (
|
|
1859
|
-
tag !== anonTag &&
|
|
1860
|
-
pin &&
|
|
1861
|
-
(currentState.state === 'X1a' || currentState.state === 'X1b')
|
|
1862
|
-
) {
|
|
1863
|
-
const placeSpec = getPlaceSpecForEnterText(position);
|
|
1864
|
-
const { resultCode: textResultCode } = await postTextEnteredEvent({
|
|
1865
|
-
sessionId,
|
|
1866
|
-
text: pin ?? '',
|
|
1867
|
-
...(memori.needsDateTime && {
|
|
1868
|
-
dateUTC: DateTime.utc().toISO() ?? undefined,
|
|
1869
|
-
}),
|
|
1870
|
-
...(placeSpec !== undefined && { place: placeSpec }),
|
|
1871
|
-
});
|
|
1872
|
-
textResult = textResultCode;
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
if (textResult === 0) {
|
|
1876
|
-
const { currentState, ...response } = await getSession(sessionId);
|
|
1877
|
-
|
|
1878
|
-
if (response.resultCode === 0 && !!currentState) {
|
|
1879
|
-
return {
|
|
1880
|
-
currentState,
|
|
1881
|
-
sessionId,
|
|
1882
|
-
...response,
|
|
1883
|
-
};
|
|
1884
|
-
}
|
|
1885
|
-
} else if ([400, 401, 403, 404, 500].includes(resultCode)) {
|
|
1886
|
-
let storageBirthDate = getLocalConfig<string | undefined>(
|
|
1887
|
-
'birthDate',
|
|
1888
|
-
undefined
|
|
1889
|
-
);
|
|
1890
|
-
|
|
1891
|
-
let referral;
|
|
1892
|
-
try {
|
|
1893
|
-
referral = (() => {
|
|
1894
|
-
return window.location.href;
|
|
1895
|
-
})();
|
|
1896
|
-
} catch {
|
|
1897
|
-
// ignore referral URL errors
|
|
1898
|
-
}
|
|
1899
|
-
|
|
1900
|
-
fetchSession({
|
|
1901
|
-
memoriID: memori.engineMemoriID ?? '',
|
|
1902
|
-
password: secret || memoriPwd || memori.secretToken,
|
|
1903
|
-
tag: tag ?? personification?.tag,
|
|
1904
|
-
pin: pin ?? personification?.pin,
|
|
1905
|
-
initialContextVars: {
|
|
1906
|
-
LANG: userLang,
|
|
1907
|
-
PATHNAME: window.location.pathname,
|
|
1908
|
-
ROUTE: window.location.pathname?.split('/')?.pop() || '',
|
|
1909
|
-
...(initialContextVars || {}),
|
|
1910
|
-
},
|
|
1911
|
-
initialQuestion,
|
|
1912
|
-
birthDate: birthDate || storageBirthDate || undefined,
|
|
1913
|
-
additionalInfo: {
|
|
1914
|
-
...(additionalInfo || {}),
|
|
1915
|
-
loginToken: resolveLoginToken(),
|
|
1916
|
-
language: (
|
|
1917
|
-
userLang ??
|
|
1918
|
-
memori.culture?.split('-')?.[0] ??
|
|
1919
|
-
'IT'
|
|
1920
|
-
).toLowerCase(),
|
|
1921
|
-
referral: referral,
|
|
1922
|
-
timeZoneOffset: new Date().getTimezoneOffset().toString(),
|
|
1923
|
-
},
|
|
1924
|
-
});
|
|
1925
|
-
} else if (!!currentState) {
|
|
1926
|
-
return {
|
|
1927
|
-
currentState,
|
|
1928
|
-
sessionId,
|
|
1929
|
-
resultCode,
|
|
1930
|
-
};
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
} catch (_e) {
|
|
1934
|
-
let err = _e as Error;
|
|
1935
|
-
return Promise.reject(err);
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
return null;
|
|
1939
|
-
};
|
|
1940
|
-
|
|
1941
|
-
/**
|
|
1942
|
-
* Timeout conversazione
|
|
1943
|
-
*/
|
|
1944
|
-
const [userInteractionTimeout, setUserInteractionTimeout] =
|
|
1945
|
-
useState<NodeJS.Timeout>();
|
|
1946
|
-
const timeoutRef = useRef<NodeJS.Timeout>();
|
|
1947
|
-
const clearInteractionTimeout = () => {
|
|
1948
|
-
if (userInteractionTimeout) {
|
|
1949
|
-
clearTimeout(userInteractionTimeout);
|
|
1950
|
-
setUserInteractionTimeout(undefined);
|
|
1951
|
-
}
|
|
1952
|
-
if (timeoutRef?.current) {
|
|
1953
|
-
clearTimeout(timeoutRef.current);
|
|
1954
|
-
timeoutRef.current = undefined;
|
|
1955
|
-
}
|
|
1956
|
-
};
|
|
1957
|
-
useEffect(() => {
|
|
1958
|
-
return () => {
|
|
1959
|
-
setHasUserActivatedSpeak(false);
|
|
1960
|
-
setClickedStart(false);
|
|
1961
|
-
sessionStartingRef.current = false;
|
|
1962
|
-
clearInteractionTimeout();
|
|
1963
|
-
timeoutRef.current = undefined;
|
|
1964
|
-
};
|
|
1965
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1966
|
-
}, []);
|
|
1967
|
-
|
|
1968
|
-
/**
|
|
1969
|
-
* Speech recognition event handlers
|
|
1970
|
-
*/
|
|
1971
|
-
const [requestedListening, setRequestedListening] = useState(false);
|
|
1972
|
-
const startListeningRef = useRef<(() => Promise<void>) | null>(null);
|
|
1973
|
-
|
|
1974
|
-
// Define TTS configuration
|
|
1975
|
-
const ttsConfig = useMemo(
|
|
1976
|
-
() => ({
|
|
1977
|
-
provider: ttsProvider,
|
|
1978
|
-
voice: getTTSVoice(
|
|
1979
|
-
userLang || memori.culture?.split('-')?.[0] || 'EN',
|
|
1980
|
-
ttsProvider,
|
|
1981
|
-
memori.voiceType as 'MALE' | 'FEMALE' | 'NEUTRAL'
|
|
1982
|
-
),
|
|
1983
|
-
tenant: tenantID,
|
|
1984
|
-
region: 'westeurope',
|
|
1985
|
-
voiceType: memori.voiceType,
|
|
1986
|
-
layout: selectedLayout,
|
|
1987
|
-
}),
|
|
1988
|
-
[ttsProvider, userLang, memori.culture, memori.voiceType, selectedLayout]
|
|
1989
|
-
);
|
|
1990
|
-
|
|
1991
|
-
const sttConfig = useMemo(
|
|
1992
|
-
() => ({
|
|
1993
|
-
provider: ttsProvider,
|
|
1994
|
-
language: getCultureCodeByLanguage(userLang),
|
|
1995
|
-
tenant: tenantID,
|
|
1996
|
-
}),
|
|
1997
|
-
[ttsProvider, userLang]
|
|
1998
|
-
);
|
|
1999
|
-
|
|
2000
|
-
// Initialize TTS hook with basic options first
|
|
2001
|
-
const {
|
|
2002
|
-
speak: ttsSpeak,
|
|
2003
|
-
stop: ttsStop,
|
|
2004
|
-
isPlaying: isPlayingAudio,
|
|
2005
|
-
speakerMuted,
|
|
2006
|
-
toggleMute,
|
|
2007
|
-
hasUserActivatedSpeak,
|
|
2008
|
-
setHasUserActivatedSpeak,
|
|
2009
|
-
} = useTTS(
|
|
2010
|
-
ttsConfig as TTSConfig,
|
|
2011
|
-
{
|
|
2012
|
-
apiUrl: `${baseUrl}/api/tts`,
|
|
2013
|
-
continuousSpeech: continuousSpeech,
|
|
2014
|
-
preview: preview,
|
|
2015
|
-
},
|
|
2016
|
-
autoStart,
|
|
2017
|
-
defaultEnableAudio,
|
|
2018
|
-
defaultSpeakerActive ?? integrationConfig?.defaultSpeakerActive ?? true
|
|
2019
|
-
);
|
|
2020
|
-
|
|
2021
|
-
// Helper function to check if audio should be played.
|
|
2022
|
-
// When defaultEnableAudio is false, default to muted so we never play before the sync effect runs (avoids audio on first conversation start when audio is disabled).
|
|
2023
|
-
const shouldPlayAudio = (text?: string) => {
|
|
2024
|
-
const currentSpeakerMuted = getLocalConfig(
|
|
2025
|
-
'muteSpeaker',
|
|
2026
|
-
!defaultEnableAudio
|
|
2027
|
-
);
|
|
2028
|
-
return (
|
|
2029
|
-
text &&
|
|
2030
|
-
text.trim() &&
|
|
2031
|
-
!preview &&
|
|
2032
|
-
!currentSpeakerMuted &&
|
|
2033
|
-
defaultEnableAudio
|
|
2034
|
-
);
|
|
2035
|
-
};
|
|
2036
|
-
|
|
2037
|
-
// Create a single, centralized function to process and send messages
|
|
2038
|
-
const processSpeechAndSendMessage = (text: string) => {
|
|
2039
|
-
// Skip if already processing or no text
|
|
2040
|
-
if (!text || text.trim().length === 0) {
|
|
2041
|
-
return;
|
|
2042
|
-
}
|
|
2043
|
-
|
|
2044
|
-
try {
|
|
2045
|
-
// Process the text
|
|
2046
|
-
const message = stripDuplicates(text);
|
|
2047
|
-
|
|
2048
|
-
if (message.length > 0) {
|
|
2049
|
-
setUserMessage('');
|
|
2050
|
-
|
|
2051
|
-
// Send the message
|
|
2052
|
-
sendMessage(message);
|
|
2053
|
-
}
|
|
2054
|
-
} catch {
|
|
2055
|
-
// ignore speech processing errors
|
|
2056
|
-
}
|
|
2057
|
-
};
|
|
2058
|
-
|
|
2059
|
-
const {
|
|
2060
|
-
isListening,
|
|
2061
|
-
|
|
2062
|
-
// Actions
|
|
2063
|
-
startRecording,
|
|
2064
|
-
stopRecording,
|
|
2065
|
-
} = useSTT(
|
|
2066
|
-
sttConfig as STTConfig,
|
|
2067
|
-
processSpeechAndSendMessage,
|
|
2068
|
-
{
|
|
2069
|
-
apiUrl: `${baseUrl}/api/stt`,
|
|
2070
|
-
// continuousRecording: continuousSpeech,
|
|
2071
|
-
// silenceTimeout: continuousSpeechTimeout,
|
|
2072
|
-
// autoStart: autoStart,
|
|
2073
|
-
},
|
|
2074
|
-
defaultEnableAudio
|
|
2075
|
-
);
|
|
2076
|
-
|
|
2077
|
-
/**
|
|
2078
|
-
* Enhanced handleSpeak that integrates with the improved useTTS hook
|
|
2079
|
-
* Uses promise-based approach for better reliability
|
|
2080
|
-
*/
|
|
2081
|
-
const handleSpeak = async (text: string) => {
|
|
2082
|
-
if (!shouldPlayAudio(text)) {
|
|
2083
|
-
const e = new CustomEvent('MemoriEndSpeak');
|
|
2084
|
-
document.dispatchEvent(e);
|
|
2085
|
-
return Promise.resolve();
|
|
2086
|
-
}
|
|
2087
|
-
|
|
2088
|
-
if (typeof stopRecording === 'function') {
|
|
2089
|
-
stopRecording();
|
|
2090
|
-
}
|
|
2091
|
-
|
|
2092
|
-
// Reset the typing flag when Memori starts speaking
|
|
2093
|
-
setHasUserTypedMessage(false);
|
|
2094
|
-
|
|
2095
|
-
const processedText = sanitizeText(text);
|
|
2096
|
-
return ttsSpeak(processedText);
|
|
2097
|
-
};
|
|
2098
|
-
/**
|
|
2099
|
-
* Integrated solution for translating dialog state and speaking
|
|
2100
|
-
* This uses promise chaining for reliable sequencing without timeouts
|
|
2101
|
-
*/
|
|
2102
|
-
const translateAndSpeak = useCallback(
|
|
2103
|
-
async (
|
|
2104
|
-
dialogState: DialogState,
|
|
2105
|
-
language: string,
|
|
2106
|
-
msg?: string,
|
|
2107
|
-
skipEmission: boolean = false
|
|
2108
|
-
) => {
|
|
2109
|
-
try {
|
|
2110
|
-
// First ensure we have a valid dialog state
|
|
2111
|
-
if (!dialogState) {
|
|
2112
|
-
return null;
|
|
2113
|
-
}
|
|
2114
|
-
|
|
2115
|
-
// Then translate the dialog state
|
|
2116
|
-
const translatedState = await translateDialogState(
|
|
2117
|
-
dialogState,
|
|
2118
|
-
language,
|
|
2119
|
-
msg,
|
|
2120
|
-
skipEmission
|
|
2121
|
-
);
|
|
2122
|
-
|
|
2123
|
-
// If we're not skipping emission and there's something to speak, speak it
|
|
2124
|
-
const textToSpeak =
|
|
2125
|
-
translatedState.translatedEmission || translatedState.emission;
|
|
2126
|
-
|
|
2127
|
-
// Always set hasUserActivatedSpeak to true when we have a valid dialog state,
|
|
2128
|
-
// regardless of audio settings, so the chat can start properly
|
|
2129
|
-
if (!hasUserActivatedSpeak) {
|
|
2130
|
-
setHasUserActivatedSpeak(true);
|
|
2131
|
-
}
|
|
2132
|
-
|
|
2133
|
-
if (textToSpeak && !skipEmission && shouldPlayAudio(textToSpeak)) {
|
|
2134
|
-
await handleSpeak(textToSpeak);
|
|
2135
|
-
}
|
|
2136
|
-
|
|
2137
|
-
return translatedState;
|
|
2138
|
-
} catch {
|
|
2139
|
-
// Still update activation state even if there's an error
|
|
2140
|
-
if (!hasUserActivatedSpeak) {
|
|
2141
|
-
setHasUserActivatedSpeak(true);
|
|
2142
|
-
}
|
|
2143
|
-
return dialogState;
|
|
2144
|
-
}
|
|
2145
|
-
},
|
|
2146
|
-
[
|
|
2147
|
-
translateDialogState,
|
|
2148
|
-
handleSpeak,
|
|
2149
|
-
hasUserActivatedSpeak,
|
|
2150
|
-
setHasUserActivatedSpeak,
|
|
2151
|
-
speakerMuted,
|
|
2152
|
-
]
|
|
2153
|
-
);
|
|
2154
|
-
|
|
2155
|
-
const processEnterTextDialogResponse = useCallback(
|
|
2156
|
-
(event: NatsDialogResponseEvent, pending: PendingEnterText) => {
|
|
2157
|
-
const {
|
|
2158
|
-
msg,
|
|
2159
|
-
typingText: pendingTypingText,
|
|
2160
|
-
useLoaderTextAsMsg,
|
|
2161
|
-
} = pending;
|
|
2162
|
-
const currentState = event.currentState;
|
|
2163
|
-
|
|
2164
|
-
if (event.resultCode !== 0 || !currentState) {
|
|
2165
|
-
if (isSessionExpiredNatsResponse(event) && pending.text) {
|
|
2166
|
-
setMemoriTyping(false);
|
|
2167
|
-
setTypingText(undefined);
|
|
2168
|
-
retryAfterExpiredSessionRef.current({
|
|
2169
|
-
text: pending.text,
|
|
2170
|
-
media: pending.media,
|
|
2171
|
-
translate: pending.translate,
|
|
2172
|
-
translatedText: pending.translatedText,
|
|
2173
|
-
hidden: pending.hidden,
|
|
2174
|
-
typingText: pending.typingText,
|
|
2175
|
-
useLoaderTextAsMsg: pending.useLoaderTextAsMsg,
|
|
2176
|
-
hasBatchQueued: pending.hasBatchQueued,
|
|
2177
|
-
expiredSessionID: sessionId,
|
|
2178
|
-
continueFromChatLogID: chatLogID,
|
|
2179
|
-
});
|
|
2180
|
-
return;
|
|
2181
|
-
}
|
|
2182
|
-
|
|
2183
|
-
if (event.resultCode === 500 && event.resultMessage) {
|
|
2184
|
-
setHistory(h => [
|
|
2185
|
-
...h,
|
|
2186
|
-
{
|
|
2187
|
-
text: 'Error: ' + event.resultMessage,
|
|
2188
|
-
emitter: 'system',
|
|
2189
|
-
fromUser: false,
|
|
2190
|
-
initial: false,
|
|
2191
|
-
contextVars: {},
|
|
2192
|
-
date: new Date().toISOString(),
|
|
2193
|
-
},
|
|
2194
|
-
]);
|
|
2195
|
-
}
|
|
2196
|
-
return;
|
|
2197
|
-
}
|
|
2198
|
-
|
|
2199
|
-
if (!msg) {
|
|
2200
|
-
return;
|
|
2201
|
-
}
|
|
2202
|
-
|
|
2203
|
-
setChatLogID(undefined);
|
|
2204
|
-
const emission =
|
|
2205
|
-
useLoaderTextAsMsg && pendingTypingText
|
|
2206
|
-
? pendingTypingText
|
|
2207
|
-
: currentState.emission ?? currentDialogState?.emission;
|
|
2208
|
-
|
|
2209
|
-
if (
|
|
2210
|
-
userLang.toLowerCase() !== language.toLowerCase() &&
|
|
2211
|
-
emission &&
|
|
2212
|
-
isMultilanguageEnabled
|
|
2213
|
-
) {
|
|
2214
|
-
currentState.emission = emission;
|
|
2215
|
-
|
|
2216
|
-
translateDialogState(currentState, userLang, msg).then(ts => {
|
|
2217
|
-
const text = ts.translatedEmission || ts.emission;
|
|
2218
|
-
if (text && shouldPlayAudio(text)) {
|
|
2219
|
-
handleSpeak(text);
|
|
2220
|
-
}
|
|
2221
|
-
});
|
|
2222
|
-
} else {
|
|
2223
|
-
setCurrentDialogState({
|
|
2224
|
-
...currentState,
|
|
2225
|
-
emission,
|
|
2226
|
-
});
|
|
2227
|
-
|
|
2228
|
-
if (emission) {
|
|
2229
|
-
pushMessage({
|
|
2230
|
-
text: emission,
|
|
2231
|
-
emitter: currentState.emitter,
|
|
2232
|
-
media: currentState.emittedMedia ?? currentState.media,
|
|
2233
|
-
llmUsage: (currentState as any).llmUsage,
|
|
2234
|
-
fromUser: false,
|
|
2235
|
-
questionAnswered: msg,
|
|
2236
|
-
generatedByAI: !!currentState.completion,
|
|
2237
|
-
contextVars: currentState.contextVars,
|
|
2238
|
-
date: currentState.currentDate,
|
|
2239
|
-
placeName: currentState.currentPlaceName,
|
|
2240
|
-
placeLatitude: currentState.currentLatitude,
|
|
2241
|
-
placeLongitude: currentState.currentLongitude,
|
|
2242
|
-
placeUncertaintyKm: currentState.currentUncertaintyKm,
|
|
2243
|
-
tag: currentState.currentTag,
|
|
2244
|
-
memoryTags: currentState.memoryTags,
|
|
2245
|
-
} as any);
|
|
2246
|
-
if (emission && shouldPlayAudio(emission)) {
|
|
2247
|
-
handleSpeak(emission);
|
|
2248
|
-
}
|
|
2249
|
-
}
|
|
2250
|
-
}
|
|
2251
|
-
},
|
|
2252
|
-
[
|
|
2253
|
-
userLang,
|
|
2254
|
-
language,
|
|
2255
|
-
isMultilanguageEnabled,
|
|
2256
|
-
currentDialogState?.emission,
|
|
2257
|
-
translateDialogState,
|
|
2258
|
-
handleSpeak,
|
|
2259
|
-
shouldPlayAudio,
|
|
2260
|
-
]
|
|
2261
|
-
);
|
|
2262
|
-
|
|
2263
|
-
const clearEnterTextPending = useCallback(
|
|
2264
|
-
(correlationID: string, pending: PendingEnterText) => {
|
|
2265
|
-
if (pending.waitForResponse?.timeoutId) {
|
|
2266
|
-
clearTimeout(pending.waitForResponse.timeoutId);
|
|
2267
|
-
}
|
|
2268
|
-
pendingEnterTextRef.current.delete(correlationID);
|
|
2269
|
-
},
|
|
2270
|
-
[]
|
|
2271
|
-
);
|
|
2272
|
-
|
|
2273
|
-
const deliverEnterTextNatsError = useCallback(
|
|
2274
|
-
(event: NatsErrorEvent) => {
|
|
2275
|
-
const correlationID = event.correlationID;
|
|
2276
|
-
let pending: PendingEnterText | undefined;
|
|
2277
|
-
|
|
2278
|
-
if (correlationID) {
|
|
2279
|
-
pending = pendingEnterTextRef.current.get(correlationID);
|
|
2280
|
-
if (pending) {
|
|
2281
|
-
clearEnterTextPending(correlationID, pending);
|
|
2282
|
-
pending.waitForResponse?.reject(
|
|
2283
|
-
new Error(
|
|
2284
|
-
event.errorMessage ?? String(event.errorCode ?? 'NATS error')
|
|
2285
|
-
)
|
|
2286
|
-
);
|
|
2287
|
-
}
|
|
2288
|
-
}
|
|
2289
|
-
|
|
2290
|
-
if (isSessionExpiredNatsError(event) && pending?.text) {
|
|
2291
|
-
setMemoriTyping(false);
|
|
2292
|
-
setTypingText(undefined);
|
|
2293
|
-
retryAfterExpiredSessionRef.current({
|
|
2294
|
-
text: pending.text,
|
|
2295
|
-
media: pending.media,
|
|
2296
|
-
translate: pending.translate,
|
|
2297
|
-
translatedText: pending.translatedText,
|
|
2298
|
-
hidden: pending.hidden,
|
|
2299
|
-
typingText: pending.typingText,
|
|
2300
|
-
useLoaderTextAsMsg: pending.useLoaderTextAsMsg,
|
|
2301
|
-
hasBatchQueued: pending.hasBatchQueued,
|
|
2302
|
-
expiredSessionID: sessionId,
|
|
2303
|
-
continueFromChatLogID: chatLogID,
|
|
2304
|
-
});
|
|
2305
|
-
return;
|
|
2306
|
-
}
|
|
2307
|
-
|
|
2308
|
-
const errorText = event.errorMessage
|
|
2309
|
-
? `Error: ${event.errorMessage}`
|
|
2310
|
-
: event.errorCode
|
|
2311
|
-
? `Error: ${event.errorCode}`
|
|
2312
|
-
: 'Error: An unexpected error occurred';
|
|
2313
|
-
|
|
2314
|
-
pushMessage({
|
|
2315
|
-
text: errorText,
|
|
2316
|
-
emitter: 'system',
|
|
2317
|
-
fromUser: false,
|
|
2318
|
-
initial: false,
|
|
2319
|
-
contextVars: {},
|
|
2320
|
-
date: new Date().toISOString(),
|
|
2321
|
-
});
|
|
2322
|
-
|
|
2323
|
-
setMemoriTyping(false);
|
|
2324
|
-
setTypingText(undefined);
|
|
2325
|
-
},
|
|
2326
|
-
[clearEnterTextPending]
|
|
2327
|
-
);
|
|
2328
|
-
|
|
2329
|
-
const deliverEnterTextNatsResponse = useCallback(
|
|
2330
|
-
(correlationID: string, event: NatsDialogResponseEvent) => {
|
|
2331
|
-
const pending = pendingEnterTextRef.current.get(correlationID);
|
|
2332
|
-
if (!pending) {
|
|
2333
|
-
bufferedNatsResponsesRef.current.set(correlationID, event);
|
|
2334
|
-
return;
|
|
2335
|
-
}
|
|
2336
|
-
|
|
2337
|
-
clearEnterTextPending(correlationID, pending);
|
|
2338
|
-
|
|
2339
|
-
if (pending.waitForResponse) {
|
|
2340
|
-
pending.waitForResponse.resolve(event);
|
|
2341
|
-
setMemoriTyping(false);
|
|
2342
|
-
setTypingText(undefined);
|
|
2343
|
-
return;
|
|
2344
|
-
}
|
|
2345
|
-
|
|
2346
|
-
processEnterTextDialogResponse(event, pending);
|
|
2347
|
-
|
|
2348
|
-
if (!pending.hasBatchQueued) {
|
|
2349
|
-
setMemoriTyping(false);
|
|
2350
|
-
setTypingText(undefined);
|
|
2351
|
-
}
|
|
2352
|
-
},
|
|
2353
|
-
[processEnterTextDialogResponse, clearEnterTextPending]
|
|
2354
|
-
);
|
|
2355
|
-
|
|
2356
|
-
const registerPendingEnterText = useCallback(
|
|
2357
|
-
(correlationID: string, pending: PendingEnterText) => {
|
|
2358
|
-
const buffered = bufferedNatsResponsesRef.current.get(correlationID);
|
|
2359
|
-
if (buffered) {
|
|
2360
|
-
bufferedNatsResponsesRef.current.delete(correlationID);
|
|
2361
|
-
pendingEnterTextRef.current.set(correlationID, pending);
|
|
2362
|
-
deliverEnterTextNatsResponse(correlationID, buffered);
|
|
2363
|
-
return;
|
|
2364
|
-
}
|
|
2365
|
-
|
|
2366
|
-
pendingEnterTextRef.current.set(correlationID, pending);
|
|
2367
|
-
},
|
|
2368
|
-
[deliverEnterTextNatsResponse]
|
|
2369
|
-
);
|
|
2370
|
-
|
|
2371
|
-
const waitForEnterTextNatsResponse = useCallback(
|
|
2372
|
-
(correlationID: string, timeoutMs = 120000) =>
|
|
2373
|
-
new Promise<NatsDialogResponseEvent>((resolve, reject) => {
|
|
2374
|
-
const timeoutId = setTimeout(() => {
|
|
2375
|
-
const current = pendingEnterTextRef.current.get(correlationID);
|
|
2376
|
-
if (current) {
|
|
2377
|
-
clearEnterTextPending(correlationID, current);
|
|
2378
|
-
}
|
|
2379
|
-
logWidgetError('NATS timeout', { correlationID, timeoutMs });
|
|
2380
|
-
reject(new Error('NATS enter-text response timeout'));
|
|
2381
|
-
}, timeoutMs);
|
|
2382
|
-
|
|
2383
|
-
registerPendingEnterText(correlationID, {
|
|
2384
|
-
stateBeforeRequest: dialogStateFingerprint(currentDialogState),
|
|
2385
|
-
waitForResponse: {
|
|
2386
|
-
resolve: event => {
|
|
2387
|
-
clearTimeout(timeoutId);
|
|
2388
|
-
resolve(event);
|
|
2389
|
-
},
|
|
2390
|
-
reject: error => {
|
|
2391
|
-
clearTimeout(timeoutId);
|
|
2392
|
-
reject(error);
|
|
2393
|
-
},
|
|
2394
|
-
timeoutId,
|
|
2395
|
-
},
|
|
2396
|
-
});
|
|
2397
|
-
}),
|
|
2398
|
-
[registerPendingEnterText, clearEnterTextPending, currentDialogState]
|
|
2399
|
-
);
|
|
2400
|
-
|
|
2401
|
-
const catchUpPendingEnterText = useCallback(async () => {
|
|
2402
|
-
if (!sessionId || pendingEnterTextRef.current.size === 0) return;
|
|
2403
|
-
|
|
2404
|
-
// Async turns are registered in send order. The current session state can
|
|
2405
|
-
// only represent the latest completed turn, so reconcile the latest one.
|
|
2406
|
-
const latest = Array.from(pendingEnterTextRef.current.entries()).pop();
|
|
2407
|
-
if (!latest) return;
|
|
2408
|
-
const [correlationID, pending] = latest;
|
|
2409
|
-
|
|
2410
|
-
try {
|
|
2411
|
-
const { currentState, resultCode, resultMessage, requestID } =
|
|
2412
|
-
await getSession(sessionId);
|
|
2413
|
-
|
|
2414
|
-
if (resultCode !== 0 || !currentState) {
|
|
2415
|
-
deliverEnterTextNatsResponse(correlationID, {
|
|
2416
|
-
eventType: 'dialog_text_entered_response',
|
|
2417
|
-
correlationID,
|
|
2418
|
-
requestID,
|
|
2419
|
-
resultCode,
|
|
2420
|
-
resultMessage,
|
|
2421
|
-
currentState,
|
|
2422
|
-
});
|
|
2423
|
-
return;
|
|
2424
|
-
}
|
|
2425
|
-
|
|
2426
|
-
if (dialogStateFingerprint(currentState) === pending.stateBeforeRequest) {
|
|
2427
|
-
console.info('[NATS] catch-up: pending turn has not completed yet');
|
|
2428
|
-
return;
|
|
2429
|
-
}
|
|
2430
|
-
|
|
2431
|
-
console.info('[NATS] catch-up: applying current engine session state');
|
|
2432
|
-
deliverEnterTextNatsResponse(correlationID, {
|
|
2433
|
-
eventType: 'dialog_text_entered_response',
|
|
2434
|
-
correlationID,
|
|
2435
|
-
requestID,
|
|
2436
|
-
resultCode,
|
|
2437
|
-
resultMessage,
|
|
2438
|
-
currentState,
|
|
2439
|
-
});
|
|
2440
|
-
} catch (error) {
|
|
2441
|
-
logWidgetError('NATS catch-up failed', error);
|
|
2442
|
-
}
|
|
2443
|
-
}, [sessionId, deliverEnterTextNatsResponse]);
|
|
2444
|
-
|
|
2445
|
-
// NATS subscription: receives progress updates and the async enter-text response.
|
|
2446
|
-
useNats({
|
|
2447
|
-
baseUrl,
|
|
2448
|
-
sessionId,
|
|
2449
|
-
onProgress: useCallback((event: NatsProgressEvent) => {
|
|
2450
|
-
if (event.message) {
|
|
2451
|
-
setTypingText(event.message);
|
|
2452
|
-
}
|
|
2453
|
-
}, []),
|
|
2454
|
-
onDialogResponse: useCallback(
|
|
2455
|
-
(event: NatsDialogResponseEvent) => {
|
|
2456
|
-
const correlationID = event.correlationID;
|
|
2457
|
-
if (!correlationID) {
|
|
2458
|
-
logWidgetError('NATS dialog response missing correlationID', event);
|
|
2459
|
-
setMemoriTyping(false);
|
|
2460
|
-
setTypingText(undefined);
|
|
2461
|
-
return;
|
|
2462
|
-
}
|
|
2463
|
-
|
|
2464
|
-
deliverEnterTextNatsResponse(correlationID, event);
|
|
2465
|
-
},
|
|
2466
|
-
[deliverEnterTextNatsResponse]
|
|
2467
|
-
),
|
|
2468
|
-
onError: deliverEnterTextNatsError,
|
|
2469
|
-
onCatchUp: catchUpPendingEnterText,
|
|
2470
|
-
});
|
|
2471
|
-
|
|
2472
|
-
const focusChatInput = () => {
|
|
2473
|
-
let textarea = document.querySelector(
|
|
2474
|
-
'#chat-fieldset textarea'
|
|
2475
|
-
) as HTMLTextAreaElement | null;
|
|
2476
|
-
if (textarea && enableFocusChatInput) {
|
|
2477
|
-
textarea.focus();
|
|
2478
|
-
} else {
|
|
2479
|
-
textarea?.blur();
|
|
2480
|
-
}
|
|
2481
|
-
};
|
|
2482
|
-
|
|
2483
|
-
/**
|
|
2484
|
-
* Focus on the chat input on mount
|
|
2485
|
-
*/
|
|
2486
|
-
useEffect(() => {
|
|
2487
|
-
// focus on chat input disabled for totem layout
|
|
2488
|
-
if (selectedLayout !== 'TOTEM') {
|
|
2489
|
-
focusChatInput();
|
|
2490
|
-
}
|
|
2491
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2492
|
-
}, [currentDialogState?.emission]);
|
|
2493
|
-
|
|
2494
|
-
const resetUIEffects = () => {
|
|
2495
|
-
try {
|
|
2496
|
-
clearInteractionTimeout();
|
|
2497
|
-
setClickedStart(false);
|
|
2498
|
-
timeoutRef.current = undefined;
|
|
2499
|
-
ttsStop();
|
|
2500
|
-
} catch {
|
|
2501
|
-
// ignore reset errors
|
|
2502
|
-
}
|
|
2503
|
-
};
|
|
2504
|
-
useEffect(() => {
|
|
2505
|
-
return () => {
|
|
2506
|
-
resetUIEffects();
|
|
2507
|
-
};
|
|
2508
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2509
|
-
}, []);
|
|
2510
|
-
useEffect(() => {
|
|
2511
|
-
document.addEventListener('MemoriResetUIEffects', resetUIEffects);
|
|
2512
|
-
|
|
2513
|
-
return () => {
|
|
2514
|
-
document.removeEventListener('MemoriResetUIEffects', resetUIEffects);
|
|
2515
|
-
};
|
|
2516
|
-
}, []);
|
|
2517
|
-
|
|
2518
|
-
useEffect(() => {
|
|
2519
|
-
// if memori is speaking, don't start listening
|
|
2520
|
-
if (
|
|
2521
|
-
!isPlayingAudio &&
|
|
2522
|
-
continuousSpeech &&
|
|
2523
|
-
(hasUserActivatedListening || !requestedListening) &&
|
|
2524
|
-
sessionId &&
|
|
2525
|
-
!hasUserTypedMessage // Don't start recording if user has typed a message
|
|
2526
|
-
) {
|
|
2527
|
-
startRecording();
|
|
2528
|
-
} else if (isPlayingAudio && isListening) {
|
|
2529
|
-
stopRecording();
|
|
2530
|
-
}
|
|
2531
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2532
|
-
}, [isPlayingAudio, hasUserActivatedListening, hasUserTypedMessage]);
|
|
2533
|
-
|
|
2534
|
-
useEffect(() => {
|
|
2535
|
-
stopRecording();
|
|
2536
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2537
|
-
}, [language]);
|
|
2538
|
-
|
|
2539
|
-
/**
|
|
2540
|
-
* Textarea send mode handlers
|
|
2541
|
-
*/
|
|
2542
|
-
const [sendOnEnter, setSendOnEnter] = useState<'keypress' | 'click'>(
|
|
2543
|
-
'keypress'
|
|
2544
|
-
);
|
|
2545
|
-
useEffect(() => {
|
|
2546
|
-
if (window.innerWidth <= 768 && hasTouchscreen()) setSendOnEnter('click');
|
|
2547
|
-
else setSendOnEnter('keypress');
|
|
2548
|
-
}, []);
|
|
2549
|
-
|
|
2550
|
-
/**
|
|
2551
|
-
* Attachments
|
|
2552
|
-
*/
|
|
2553
|
-
const [attachmentsMenuOpen, setAttachmentsMenuOpen] = useState<
|
|
2554
|
-
'link' | 'media'
|
|
2555
|
-
>();
|
|
2556
|
-
|
|
2557
|
-
const globalBackground = integrationConfig?.globalBackground;
|
|
2558
|
-
const globalBackgroundUrl = globalBackground
|
|
2559
|
-
? `url(${globalBackground})`
|
|
2560
|
-
: null;
|
|
2561
|
-
|
|
2562
|
-
const integrationProperties = (
|
|
2563
|
-
integration
|
|
2564
|
-
? {
|
|
2565
|
-
'--memori-chat-bubble-bg': '#fff',
|
|
2566
|
-
...(integrationConfig && !instruct
|
|
2567
|
-
? { '--memori-text-color': integrationConfig.textColor ?? '#000' }
|
|
2568
|
-
: {}),
|
|
2569
|
-
...(integrationConfig?.buttonBgColor
|
|
2570
|
-
? {
|
|
2571
|
-
'--memori-button-bg': integrationConfig.buttonBgColor,
|
|
2572
|
-
'--memori-primary': integrationConfig.buttonBgColor,
|
|
2573
|
-
}
|
|
2574
|
-
: {}),
|
|
2575
|
-
...(integrationConfig?.buttonTextColor
|
|
2576
|
-
? {
|
|
2577
|
-
'--memori-button-text': integrationConfig.buttonTextColor,
|
|
2578
|
-
}
|
|
2579
|
-
: {}),
|
|
2580
|
-
...(integrationConfig?.blurBackground
|
|
2581
|
-
? {
|
|
2582
|
-
'--memori-blur-background': '5px',
|
|
2583
|
-
}
|
|
2584
|
-
: {
|
|
2585
|
-
'--memori-blur-background': '0px',
|
|
2586
|
-
}),
|
|
2587
|
-
...(integrationConfig?.innerBgColor
|
|
2588
|
-
? {
|
|
2589
|
-
'--memori-inner-bg': `rgba(${
|
|
2590
|
-
integrationConfig.innerBgColor === 'dark'
|
|
2591
|
-
? '0, 0, 0'
|
|
2592
|
-
: '255, 255, 255'
|
|
2593
|
-
}, ${integrationConfig.innerBgAlpha ?? 0.4})`,
|
|
2594
|
-
'--memori-inner-content-pad': '1.5rem',
|
|
2595
|
-
'--memori-nav-bg-image': 'none',
|
|
2596
|
-
'--memori-nav-bg': `rgba(${
|
|
2597
|
-
integrationConfig.innerBgColor === 'dark'
|
|
2598
|
-
? '0, 0, 0'
|
|
2599
|
-
: '255, 255, 255'
|
|
2600
|
-
}, ${integrationConfig?.innerBgAlpha ?? 0.4})`,
|
|
2601
|
-
}
|
|
2602
|
-
: {
|
|
2603
|
-
'--memori-inner-content-pad': '0px',
|
|
2604
|
-
}),
|
|
2605
|
-
}
|
|
2606
|
-
: {}
|
|
2607
|
-
) as CSSProperties;
|
|
2608
|
-
|
|
2609
|
-
const integrationStylesheet = `
|
|
2610
|
-
${
|
|
2611
|
-
preview ? '#preview, ' : applyVarsToRoot ? ':root, ' : ''
|
|
2612
|
-
}memori-client, .memori-widget, .memori-drawer, .memori-modal {
|
|
2613
|
-
${Object.entries(integrationProperties)
|
|
2614
|
-
.map(([key, value]) => `${key}: ${value};`)
|
|
2615
|
-
.join('\n')}
|
|
2616
|
-
}
|
|
2617
|
-
`;
|
|
2618
|
-
|
|
2619
|
-
const showAIicon =
|
|
2620
|
-
integrationConfig?.showAIicon === undefined
|
|
2621
|
-
? true
|
|
2622
|
-
: integrationConfig?.showAIicon;
|
|
2623
|
-
|
|
2624
|
-
const enableUpload = !!(showUpload ?? integrationConfig?.showUpload);
|
|
2625
|
-
|
|
2626
|
-
const enableReasoning = !!(showReasoning ?? integrationConfig?.showReasoning);
|
|
2627
|
-
const enableMessageConsumption = !!runtimeShowMessageConsumption;
|
|
2628
|
-
|
|
2629
|
-
const showWhyThisAnswer =
|
|
2630
|
-
integrationConfig?.showWhyThisAnswer === undefined
|
|
2631
|
-
? true
|
|
2632
|
-
: integrationConfig?.showWhyThisAnswer;
|
|
2633
|
-
|
|
2634
|
-
// eslint-disable-next-line
|
|
2635
|
-
const [avatar3dVisible, setAvatar3dVisible] = useState(false);
|
|
2636
|
-
useEffect(() => {
|
|
2637
|
-
if (
|
|
2638
|
-
(window.innerWidth >= 768 && selectedLayout === 'FULLPAGE') ||
|
|
2639
|
-
selectedLayout !== 'FULLPAGE'
|
|
2640
|
-
) {
|
|
2641
|
-
setAvatar3dVisible(true);
|
|
2642
|
-
}
|
|
2643
|
-
}, []);
|
|
2644
|
-
|
|
2645
|
-
// Put SEO tags in head
|
|
2646
|
-
useEffect(() => {
|
|
2647
|
-
if (integrationConfig?.seoTitle) {
|
|
2648
|
-
let meta = document.createElement('meta');
|
|
2649
|
-
meta.setAttribute('property', 'og:title');
|
|
2650
|
-
meta.setAttribute('content', integrationConfig.seoTitle);
|
|
2651
|
-
document.head.append(meta);
|
|
2652
|
-
}
|
|
2653
|
-
if (integrationConfig?.seoDescription) {
|
|
2654
|
-
let meta = document.createElement('meta');
|
|
2655
|
-
meta.setAttribute('property', 'og:description');
|
|
2656
|
-
meta.setAttribute('content', integrationConfig.seoDescription);
|
|
2657
|
-
document.head.append(meta);
|
|
2658
|
-
}
|
|
2659
|
-
if (integrationConfig?.seoUrl) {
|
|
2660
|
-
let meta = document.createElement('meta');
|
|
2661
|
-
meta.setAttribute('property', 'og:url');
|
|
2662
|
-
meta.setAttribute('content', integrationConfig.seoUrl);
|
|
2663
|
-
document.head.append(meta);
|
|
2664
|
-
}
|
|
2665
|
-
let image = ogImage || memori.avatarURL;
|
|
2666
|
-
if (integrationConfig?.seoImageShowAvatar && image) {
|
|
2667
|
-
let meta = document.createElement('meta');
|
|
2668
|
-
meta.setAttribute('property', 'og:image');
|
|
2669
|
-
meta.setAttribute('content', image);
|
|
2670
|
-
document.head.append(meta);
|
|
2671
|
-
}
|
|
2672
|
-
}, [integrationConfig, memori.avatarURL, ogImage]);
|
|
2673
|
-
|
|
2674
|
-
const simulateUserPrompt = (text: string, translatedText?: string) => {
|
|
2675
|
-
ttsStop();
|
|
2676
|
-
sendMessage(text, undefined, undefined, false, translatedText);
|
|
2677
|
-
};
|
|
2678
|
-
|
|
2679
|
-
// listen to events from browser
|
|
2680
|
-
// to use in integrations or snippets
|
|
2681
|
-
const memoriTextEnteredHandler = useCallback(
|
|
2682
|
-
(e: MemoriTextEnteredEvent) => {
|
|
2683
|
-
if (disableTextEnteredEvents) {
|
|
2684
|
-
return;
|
|
2685
|
-
}
|
|
2686
|
-
|
|
2687
|
-
const {
|
|
2688
|
-
text,
|
|
2689
|
-
waitForPrevious,
|
|
2690
|
-
hidden,
|
|
2691
|
-
typingText,
|
|
2692
|
-
useLoaderTextAsMsg,
|
|
2693
|
-
hasBatchQueued,
|
|
2694
|
-
} = e.detail;
|
|
2695
|
-
|
|
2696
|
-
if (text) {
|
|
2697
|
-
// wait to finish reading previous emission
|
|
2698
|
-
if (
|
|
2699
|
-
waitForPrevious &&
|
|
2700
|
-
!speakerMuted &&
|
|
2701
|
-
(memoriSpeaking || !!memoriTyping)
|
|
2702
|
-
) {
|
|
2703
|
-
setTimeout(() => {
|
|
2704
|
-
memoriTextEnteredHandler(e);
|
|
2705
|
-
}, 1000);
|
|
2706
|
-
} else {
|
|
2707
|
-
ttsStop();
|
|
2708
|
-
sendMessage(
|
|
2709
|
-
text,
|
|
2710
|
-
undefined,
|
|
2711
|
-
undefined,
|
|
2712
|
-
undefined,
|
|
2713
|
-
undefined,
|
|
2714
|
-
hidden,
|
|
2715
|
-
typingText,
|
|
2716
|
-
useLoaderTextAsMsg,
|
|
2717
|
-
hasBatchQueued
|
|
2718
|
-
);
|
|
2719
|
-
}
|
|
2720
|
-
}
|
|
2721
|
-
},
|
|
2722
|
-
[
|
|
2723
|
-
sessionId,
|
|
2724
|
-
isPlayingAudio,
|
|
2725
|
-
memoriTyping,
|
|
2726
|
-
userLang,
|
|
2727
|
-
disableTextEnteredEvents,
|
|
2728
|
-
speakerMuted,
|
|
2729
|
-
]
|
|
2730
|
-
);
|
|
2731
|
-
useEffect(() => {
|
|
2732
|
-
if (!disableTextEnteredEvents) {
|
|
2733
|
-
document.addEventListener('MemoriTextEntered', memoriTextEnteredHandler);
|
|
2734
|
-
} else {
|
|
2735
|
-
document.removeEventListener(
|
|
2736
|
-
'MemoriTextEntered',
|
|
2737
|
-
memoriTextEnteredHandler
|
|
2738
|
-
);
|
|
2739
|
-
}
|
|
2740
|
-
|
|
2741
|
-
return () => {
|
|
2742
|
-
document.removeEventListener(
|
|
2743
|
-
'MemoriTextEntered',
|
|
2744
|
-
memoriTextEnteredHandler
|
|
2745
|
-
);
|
|
2746
|
-
};
|
|
2747
|
-
}, [sessionId, userLang, disableTextEnteredEvents]);
|
|
2748
|
-
|
|
2749
|
-
/**
|
|
2750
|
-
* Handles clicking the start button to begin or resume a session
|
|
2751
|
-
* @param session Optional existing session with dialog state and ID
|
|
2752
|
-
* @param initialSessionExpired Whether the initial session has expired
|
|
2753
|
-
/**
|
|
2754
|
-
* Handles clicking the start button to begin or resume a session
|
|
2755
|
-
* @param session Optional existing session with dialog state and ID
|
|
2756
|
-
* @param initialSessionExpired Whether the initial session has expired
|
|
2757
|
-
*/
|
|
2758
|
-
const onClickStart = useCallback(
|
|
2759
|
-
async (
|
|
2760
|
-
session?: { dialogState: DialogState; sessionID: string },
|
|
2761
|
-
initialSessionExpired = false,
|
|
2762
|
-
chatLog?: ChatLog,
|
|
2763
|
-
targetSessionID?: string
|
|
2764
|
-
) => {
|
|
2765
|
-
const sessionID = chatLog ? undefined : session?.sessionID || sessionId;
|
|
2766
|
-
const dialogState = chatLog
|
|
2767
|
-
? undefined
|
|
2768
|
-
: session?.dialogState || currentDialogState;
|
|
2769
|
-
setClickedStart(true);
|
|
2770
|
-
setHasUserTypedMessage(false); // Reset typing flag when starting a new session
|
|
2771
|
-
|
|
2772
|
-
let translatedMessages: Message[] = [];
|
|
2773
|
-
|
|
2774
|
-
// Get birth date from storage or props
|
|
2775
|
-
let storageBirthDate = getLocalConfig<string | undefined>(
|
|
2776
|
-
'birthDate',
|
|
2777
|
-
undefined
|
|
2778
|
-
);
|
|
2779
|
-
let birth = birthDate || storageBirthDate || user?.birthDate;
|
|
2780
|
-
if (!birth && autoStart && (initialSessionID || targetSessionID))
|
|
2781
|
-
birth = '1970-01-01T10:24:03.845Z';
|
|
2782
|
-
|
|
2783
|
-
const localPosition = getLocalConfig<Venue | undefined>(
|
|
2784
|
-
'position',
|
|
2785
|
-
undefined
|
|
2786
|
-
);
|
|
2787
|
-
// Only check for position requirement if memori.needsPosition is true
|
|
2788
|
-
if (autoStart && !localPosition && memori.needsPosition) {
|
|
2789
|
-
setShowPositionDrawer(true);
|
|
2790
|
-
return;
|
|
2791
|
-
}
|
|
2792
|
-
|
|
2793
|
-
if (!(await checkCredits({ notify: true }))) {
|
|
2794
|
-
setClickedStart(false);
|
|
2795
|
-
setLoading(false);
|
|
2796
|
-
return;
|
|
2797
|
-
}
|
|
2798
|
-
|
|
2799
|
-
// Handle age verification
|
|
2800
|
-
if (!sessionID && !!minAge && !birth) {
|
|
2801
|
-
setShowAgeVerification(true);
|
|
2802
|
-
setClickedStart(false);
|
|
2803
|
-
return;
|
|
2804
|
-
}
|
|
2805
|
-
// Handle authentication
|
|
2806
|
-
else if (
|
|
2807
|
-
!sessionID &&
|
|
2808
|
-
memori.privacyType !== 'PUBLIC' &&
|
|
2809
|
-
!memori.secretToken &&
|
|
2810
|
-
!memoriPwd &&
|
|
2811
|
-
!memoriTokens
|
|
2812
|
-
) {
|
|
2813
|
-
setAuthModalState('password');
|
|
2814
|
-
setClickedStart(false);
|
|
2815
|
-
return;
|
|
2816
|
-
}
|
|
2817
|
-
// Create new session if needed
|
|
2818
|
-
else if (!sessionID || initialSessionExpired) {
|
|
2819
|
-
if (sessionStartingRef.current) {
|
|
2820
|
-
return;
|
|
2821
|
-
}
|
|
2822
|
-
sessionStartingRef.current = true;
|
|
2823
|
-
try {
|
|
2824
|
-
const session = await fetchSession({
|
|
2825
|
-
memoriID: memori.engineMemoriID!,
|
|
2826
|
-
password: secret || memoriPwd || memori.secretToken,
|
|
2827
|
-
tag: personification?.tag,
|
|
2828
|
-
pin: personification?.pin,
|
|
2829
|
-
continueFromChatLogID: chatLog?.chatLogID,
|
|
2830
|
-
initialContextVars: {
|
|
2831
|
-
LANG: userLang,
|
|
2832
|
-
PATHNAME: window.location.pathname?.toUpperCase(),
|
|
2833
|
-
ROUTE:
|
|
2834
|
-
window.location.pathname?.split('/')?.pop()?.toUpperCase() ||
|
|
2835
|
-
'',
|
|
2836
|
-
...((!chatLog
|
|
2837
|
-
? initialContextVars
|
|
2838
|
-
: chatLog.lines[chatLog.lines.length - 1].contextVars) || {}),
|
|
2839
|
-
},
|
|
2840
|
-
initialQuestion: chatLog ? undefined : initialQuestion,
|
|
2841
|
-
birthDate: birth,
|
|
2842
|
-
additionalInfo: {
|
|
2843
|
-
...(additionalInfo || {}),
|
|
2844
|
-
loginToken: resolveLoginToken(),
|
|
2845
|
-
language: (
|
|
2846
|
-
userLang ??
|
|
2847
|
-
memori.culture?.split('-')?.[0] ??
|
|
2848
|
-
'IT'
|
|
2849
|
-
).toLowerCase(),
|
|
2850
|
-
timeZoneOffset: new Date().getTimezoneOffset().toString(),
|
|
2851
|
-
},
|
|
2852
|
-
});
|
|
2853
|
-
|
|
2854
|
-
if (session?.dialogState) {
|
|
2855
|
-
// reset history
|
|
2856
|
-
if (!chatLog) {
|
|
2857
|
-
setHistory([]);
|
|
2858
|
-
|
|
2859
|
-
// Use translateAndSpeak which already handles the speaking
|
|
2860
|
-
await translateAndSpeak(session.dialogState, userLang);
|
|
2861
|
-
// No need for additional handleSpeak call since translateAndSpeak already handles it
|
|
2862
|
-
setHasUserActivatedSpeak(true);
|
|
2863
|
-
setClickedStart(false);
|
|
2864
|
-
} else {
|
|
2865
|
-
const messages = chatLog.lines.map(
|
|
2866
|
-
(l, i) =>
|
|
2867
|
-
({
|
|
2868
|
-
text: l.text,
|
|
2869
|
-
media: l.media
|
|
2870
|
-
?.filter(m => allowedMediaTypes.includes(m.mimeType))
|
|
2871
|
-
?.map(m => ({
|
|
2872
|
-
mediumID: `${i}-${m.mimeType}`,
|
|
2873
|
-
...m,
|
|
2874
|
-
})),
|
|
2875
|
-
fromUser: l.inbound,
|
|
2876
|
-
llmUsage: (l as any).llmUsage,
|
|
2877
|
-
timestamp: l.timestamp,
|
|
2878
|
-
emitter: l.emitter,
|
|
2879
|
-
initial: i === 0,
|
|
2880
|
-
} as Message)
|
|
2881
|
-
);
|
|
2882
|
-
|
|
2883
|
-
// we dont remove the last one as it is the current state
|
|
2884
|
-
translatedMessages = messages ?? [];
|
|
2885
|
-
if (
|
|
2886
|
-
language.toUpperCase() !== userLang.toUpperCase() &&
|
|
2887
|
-
isMultilanguageEnabled
|
|
2888
|
-
) {
|
|
2889
|
-
try {
|
|
2890
|
-
translatedMessages = await Promise.all(
|
|
2891
|
-
messages.map(async m => {
|
|
2892
|
-
// If original text is present, the message is already translated
|
|
2893
|
-
if ('originalText' in m && m.originalText) {
|
|
2894
|
-
return m;
|
|
2895
|
-
}
|
|
2896
|
-
// Otherwise translate the message
|
|
2897
|
-
return {
|
|
2898
|
-
...m,
|
|
2899
|
-
originalText: m.text,
|
|
2900
|
-
text: (
|
|
2901
|
-
await getTranslation(
|
|
2902
|
-
m.text,
|
|
2903
|
-
userLang,
|
|
2904
|
-
language,
|
|
2905
|
-
baseUrl
|
|
2906
|
-
)
|
|
2907
|
-
).text,
|
|
2908
|
-
};
|
|
2909
|
-
})
|
|
2910
|
-
);
|
|
2911
|
-
} catch {
|
|
2912
|
-
// ignore translation errors
|
|
2913
|
-
}
|
|
2914
|
-
}
|
|
2915
|
-
|
|
2916
|
-
setHistory(translatedMessages);
|
|
2917
|
-
|
|
2918
|
-
translateDialogState(
|
|
2919
|
-
session.dialogState,
|
|
2920
|
-
userLang,
|
|
2921
|
-
undefined,
|
|
2922
|
-
true
|
|
2923
|
-
).finally(() => {
|
|
2924
|
-
setHasUserActivatedSpeak(true);
|
|
2925
|
-
setClickedStart(false);
|
|
2926
|
-
});
|
|
2927
|
-
}
|
|
2928
|
-
} else if (session?.resultCode === 0) {
|
|
2929
|
-
sessionStartingRef.current = false;
|
|
2930
|
-
await onClickStart((session as any) || undefined);
|
|
2931
|
-
} else {
|
|
2932
|
-
setLoading(false);
|
|
2933
|
-
setClickedStart(false);
|
|
2934
|
-
}
|
|
2935
|
-
} finally {
|
|
2936
|
-
sessionStartingRef.current = false;
|
|
2937
|
-
}
|
|
2938
|
-
|
|
2939
|
-
return;
|
|
2940
|
-
}
|
|
2941
|
-
// Handle initial session
|
|
2942
|
-
else if (initialSessionID || targetSessionID) {
|
|
2943
|
-
const sessionID = targetSessionID ?? initialSessionID;
|
|
2944
|
-
// check if session is valid and not expired
|
|
2945
|
-
const { currentState, ...response } = await getSession(sessionID!);
|
|
2946
|
-
|
|
2947
|
-
if (response.resultCode !== 0 || !currentState) {
|
|
2948
|
-
const { chatLogs } = await getSessionChatLogs(sessionID!, sessionID!);
|
|
2949
|
-
setSessionId(undefined);
|
|
2950
|
-
await onClickStart(undefined, true, chatLogs?.[0]);
|
|
2951
|
-
return;
|
|
2952
|
-
}
|
|
2953
|
-
|
|
2954
|
-
// reset history
|
|
2955
|
-
setHistory([]);
|
|
2956
|
-
|
|
2957
|
-
// Handle personification tag changes
|
|
2958
|
-
if (
|
|
2959
|
-
personification &&
|
|
2960
|
-
currentState.currentTag !== personification.tag
|
|
2961
|
-
) {
|
|
2962
|
-
try {
|
|
2963
|
-
// reset tag
|
|
2964
|
-
await changeTag(memori.engineMemoriID!, sessionID!, '-');
|
|
2965
|
-
// change tag to receiver
|
|
2966
|
-
const session = await changeTag(
|
|
2967
|
-
memori.engineMemoriID!,
|
|
2968
|
-
sessionID!,
|
|
2969
|
-
personification.tag,
|
|
2970
|
-
personification.pin
|
|
2971
|
-
);
|
|
2972
|
-
|
|
2973
|
-
if (session && session.resultCode === 0) {
|
|
2974
|
-
await translateAndSpeak(session.currentState, userLang);
|
|
2975
|
-
setClickedStart(false);
|
|
2976
|
-
} else {
|
|
2977
|
-
throw new Error('No session');
|
|
2978
|
-
}
|
|
2979
|
-
} catch {
|
|
2980
|
-
reopenSession(
|
|
2981
|
-
true,
|
|
2982
|
-
memori?.secretToken,
|
|
2983
|
-
undefined,
|
|
2984
|
-
personification.tag,
|
|
2985
|
-
personification.pin,
|
|
2986
|
-
{
|
|
2987
|
-
LANG: userLang,
|
|
2988
|
-
PATHNAME: window.location.pathname?.toUpperCase(),
|
|
2989
|
-
ROUTE:
|
|
2990
|
-
window.location.pathname?.split('/')?.pop()?.toUpperCase() ||
|
|
2991
|
-
'',
|
|
2992
|
-
...(initialContextVars || {}),
|
|
2993
|
-
},
|
|
2994
|
-
initialQuestion,
|
|
2995
|
-
birth
|
|
2996
|
-
).then(() => {
|
|
2997
|
-
setHasUserActivatedSpeak(true);
|
|
2998
|
-
setClickedStart(false);
|
|
2999
|
-
});
|
|
3000
|
-
}
|
|
3001
|
-
}
|
|
3002
|
-
// Handle anonymous tag changes
|
|
3003
|
-
else if (
|
|
3004
|
-
!personification &&
|
|
3005
|
-
currentState?.currentTag &&
|
|
3006
|
-
currentState?.currentTag !== anonTag &&
|
|
3007
|
-
currentState?.currentTag !== '-'
|
|
3008
|
-
) {
|
|
3009
|
-
try {
|
|
3010
|
-
// reset tag
|
|
3011
|
-
await changeTag(memori.engineMemoriID!, sessionID!, '-');
|
|
3012
|
-
// change tag to anonymous
|
|
3013
|
-
const session = await changeTag(
|
|
3014
|
-
memori.engineMemoriID!,
|
|
3015
|
-
sessionID!,
|
|
3016
|
-
anonTag
|
|
3017
|
-
);
|
|
3018
|
-
|
|
3019
|
-
if (session && session.resultCode === 0) {
|
|
3020
|
-
await translateAndSpeak(session.currentState, userLang);
|
|
3021
|
-
setClickedStart(false);
|
|
3022
|
-
} else {
|
|
3023
|
-
throw new Error('No session');
|
|
3024
|
-
}
|
|
3025
|
-
} catch (e) {
|
|
3026
|
-
reopenSession(
|
|
3027
|
-
true,
|
|
3028
|
-
memori?.secretToken,
|
|
3029
|
-
undefined,
|
|
3030
|
-
undefined,
|
|
3031
|
-
undefined,
|
|
3032
|
-
{
|
|
3033
|
-
LANG: userLang,
|
|
3034
|
-
PATHNAME: window.location.pathname?.toUpperCase(),
|
|
3035
|
-
ROUTE:
|
|
3036
|
-
window.location.pathname?.split('/')?.pop()?.toUpperCase() ||
|
|
3037
|
-
'',
|
|
3038
|
-
...(initialContextVars || {}),
|
|
3039
|
-
},
|
|
3040
|
-
initialQuestion,
|
|
3041
|
-
birth
|
|
3042
|
-
).then(() => {
|
|
3043
|
-
setHasUserActivatedSpeak(true);
|
|
3044
|
-
setClickedStart(false);
|
|
3045
|
-
});
|
|
3046
|
-
}
|
|
3047
|
-
}
|
|
3048
|
-
// No tag changes needed
|
|
3049
|
-
else {
|
|
3050
|
-
try {
|
|
3051
|
-
//This is the session id of the session that was opened before the current session
|
|
3052
|
-
const { chatLogs } = await getSessionChatLogs(
|
|
3053
|
-
sessionID!,
|
|
3054
|
-
sessionID!
|
|
3055
|
-
);
|
|
3056
|
-
|
|
3057
|
-
const messages = chatLogs?.[0]?.lines.map(
|
|
3058
|
-
(l, i) =>
|
|
3059
|
-
({
|
|
3060
|
-
text: l.text,
|
|
3061
|
-
media: l.media
|
|
3062
|
-
?.filter(m => allowedMediaTypes.includes(m.mimeType))
|
|
3063
|
-
?.map(m => ({
|
|
3064
|
-
mediumID: `${i}-${m.mimeType}`,
|
|
3065
|
-
...m,
|
|
3066
|
-
})),
|
|
3067
|
-
fromUser: l.inbound,
|
|
3068
|
-
llmUsage: (l as any).llmUsage,
|
|
3069
|
-
timestamp: l.timestamp,
|
|
3070
|
-
emitter: l.emitter,
|
|
3071
|
-
initial: i === 0,
|
|
3072
|
-
} as Message)
|
|
3073
|
-
);
|
|
3074
|
-
|
|
3075
|
-
// we dont remove the last one as it is the current state
|
|
3076
|
-
translatedMessages = messages ?? [];
|
|
3077
|
-
if (
|
|
3078
|
-
language.toUpperCase() !== userLang.toUpperCase() &&
|
|
3079
|
-
isMultilanguageEnabled
|
|
3080
|
-
) {
|
|
3081
|
-
try {
|
|
3082
|
-
translatedMessages = await Promise.all(
|
|
3083
|
-
messages.map(async m => ({
|
|
3084
|
-
...m,
|
|
3085
|
-
originalText: m.text,
|
|
3086
|
-
text: (
|
|
3087
|
-
await getTranslation(m.text, userLang, language, baseUrl)
|
|
3088
|
-
).text,
|
|
3089
|
-
}))
|
|
3090
|
-
);
|
|
3091
|
-
} catch {
|
|
3092
|
-
// ignore translation errors
|
|
3093
|
-
}
|
|
3094
|
-
}
|
|
3095
|
-
|
|
3096
|
-
setHistory(translatedMessages);
|
|
3097
|
-
} catch {
|
|
3098
|
-
// ignore chat log retrieval errors
|
|
3099
|
-
}
|
|
3100
|
-
|
|
3101
|
-
if (
|
|
3102
|
-
(!!translatedMessages?.length && translatedMessages.length > 1) ||
|
|
3103
|
-
!initialQuestion
|
|
3104
|
-
) {
|
|
3105
|
-
// we have a history, don't push message
|
|
3106
|
-
setHasUserActivatedSpeak(true);
|
|
3107
|
-
setClickedStart(false);
|
|
3108
|
-
await translateAndSpeak(
|
|
3109
|
-
currentState,
|
|
3110
|
-
userLang,
|
|
3111
|
-
undefined,
|
|
3112
|
-
// if empty history, pick current state emission
|
|
3113
|
-
// otherwise, don't push message
|
|
3114
|
-
!!translatedMessages?.length
|
|
3115
|
-
);
|
|
3116
|
-
} else {
|
|
3117
|
-
// remove default initial message
|
|
3118
|
-
translatedMessages = [];
|
|
3119
|
-
setHistory([]);
|
|
3120
|
-
|
|
3121
|
-
// we have no chat history, we start by initial question
|
|
3122
|
-
const placeSpec = getPlaceSpecForEnterText(position);
|
|
3123
|
-
const response = await postEnterTextAsync({
|
|
3124
|
-
sessionId: sessionID!,
|
|
3125
|
-
text: initialQuestion,
|
|
3126
|
-
...(memori.needsDateTime && {
|
|
3127
|
-
dateUTC: DateTime.utc().toISO() ?? undefined,
|
|
3128
|
-
}),
|
|
3129
|
-
...(placeSpec !== undefined && { place: placeSpec }),
|
|
3130
|
-
});
|
|
3131
|
-
// Handle 500 error from EnterTextAsync
|
|
3132
|
-
if (response.resultCode === 500 && response.resultMessage) {
|
|
3133
|
-
setHistory(h => [
|
|
3134
|
-
...h,
|
|
3135
|
-
{
|
|
3136
|
-
text: 'Error: ' + response.resultMessage,
|
|
3137
|
-
emitter: 'system',
|
|
3138
|
-
fromUser: false,
|
|
3139
|
-
initial: false,
|
|
3140
|
-
contextVars: {},
|
|
3141
|
-
date: new Date().toISOString(),
|
|
3142
|
-
},
|
|
3143
|
-
]);
|
|
3144
|
-
return;
|
|
3145
|
-
}
|
|
3146
|
-
|
|
3147
|
-
const onClickStartCorrelationID = readCorrelationID(response);
|
|
3148
|
-
if (response.resultCode === 0 && onClickStartCorrelationID) {
|
|
3149
|
-
setMemoriTyping(true);
|
|
3150
|
-
try {
|
|
3151
|
-
const natsEvent = await waitForEnterTextNatsResponse(
|
|
3152
|
-
onClickStartCorrelationID
|
|
3153
|
-
);
|
|
3154
|
-
if (natsEvent.resultCode === 0 && natsEvent.currentState) {
|
|
3155
|
-
await translateAndSpeak(
|
|
3156
|
-
natsEvent.currentState,
|
|
3157
|
-
userLang,
|
|
3158
|
-
undefined,
|
|
3159
|
-
false
|
|
3160
|
-
);
|
|
3161
|
-
setClickedStart(false);
|
|
3162
|
-
}
|
|
3163
|
-
} catch (err) {
|
|
3164
|
-
logWidgetError('onClickStart NATS wait failed', err);
|
|
3165
|
-
setMemoriTyping(false);
|
|
3166
|
-
setTypingText(undefined);
|
|
3167
|
-
}
|
|
3168
|
-
} else if (response.resultCode === 0) {
|
|
3169
|
-
logWidgetError('onClickStart enter-text missing correlationID', response);
|
|
3170
|
-
}
|
|
3171
|
-
}
|
|
3172
|
-
}
|
|
3173
|
-
}
|
|
3174
|
-
// Default case - just translate and activate
|
|
3175
|
-
else {
|
|
3176
|
-
// reset history
|
|
3177
|
-
setHistory([]);
|
|
3178
|
-
|
|
3179
|
-
// everything is fine, just translate dialog state and activate chat
|
|
3180
|
-
await translateAndSpeak(dialogState!, userLang);
|
|
3181
|
-
setClickedStart(false);
|
|
3182
|
-
}
|
|
3183
|
-
},
|
|
3184
|
-
[memoriPwd, memori, memoriTokens, birthDate, sessionId, userLang, position]
|
|
3185
|
-
);
|
|
3186
|
-
|
|
3187
|
-
// check if owner has enough credits
|
|
3188
|
-
const needsCredits = tenant?.billingDelegation;
|
|
3189
|
-
const [hasEnoughCredits, setHasEnoughCredits] = useState<boolean>(true);
|
|
3190
|
-
|
|
3191
|
-
useEffect(() => {
|
|
3192
|
-
if (
|
|
3193
|
-
!clickedStart &&
|
|
3194
|
-
!sessionStartingRef.current &&
|
|
3195
|
-
!sessionId &&
|
|
3196
|
-
autoStart &&
|
|
3197
|
-
selectedLayout !== 'HIDDEN_CHAT' &&
|
|
3198
|
-
(!needsCredits || hasEnoughCredits)
|
|
3199
|
-
) {
|
|
3200
|
-
onClickStart();
|
|
3201
|
-
}
|
|
3202
|
-
}, [
|
|
3203
|
-
clickedStart,
|
|
3204
|
-
autoStart,
|
|
3205
|
-
selectedLayout,
|
|
3206
|
-
sessionId,
|
|
3207
|
-
needsCredits,
|
|
3208
|
-
hasEnoughCredits,
|
|
3209
|
-
]);
|
|
3210
|
-
|
|
3211
|
-
useEffect(() => {
|
|
3212
|
-
const targetNode =
|
|
3213
|
-
document.querySelector(`memori-client[memoriname="${memori.name}"]`) ||
|
|
3214
|
-
document.querySelector(`memori-client[memoriid="${memori.memoriID}"]`) ||
|
|
3215
|
-
document.querySelector('memori-client');
|
|
3216
|
-
if (!targetNode) {
|
|
3217
|
-
return;
|
|
3218
|
-
}
|
|
3219
|
-
|
|
3220
|
-
const propsToken = additionalInfo?.loginToken ?? authToken;
|
|
3221
|
-
const initialDomToken = targetNode.getAttribute('authtoken') || undefined;
|
|
3222
|
-
if (initialDomToken && !propsToken) {
|
|
3223
|
-
setLoginToken(initialDomToken);
|
|
3224
|
-
userTokenRef.current = initialDomToken;
|
|
3225
|
-
}
|
|
3226
|
-
|
|
3227
|
-
const applyAuthTokenFromElement = (element: Element) => {
|
|
3228
|
-
const clientNode =
|
|
3229
|
-
element.nodeName === 'MEMORI-CLIENT'
|
|
3230
|
-
? element
|
|
3231
|
-
: element.closest('memori-client') ?? element.parentElement;
|
|
3232
|
-
const token = clientNode?.getAttribute('authtoken') || undefined;
|
|
3233
|
-
setLoginToken(token);
|
|
3234
|
-
userTokenRef.current = token;
|
|
3235
|
-
};
|
|
3236
|
-
|
|
3237
|
-
const config = { attributes: true, childList: false, subtree: false };
|
|
3238
|
-
const callback: MutationCallback = mutationList => {
|
|
3239
|
-
for (const mutation of mutationList) {
|
|
3240
|
-
if (
|
|
3241
|
-
mutation.type === 'attributes' &&
|
|
3242
|
-
mutation.attributeName?.toLowerCase() === 'authtoken'
|
|
3243
|
-
) {
|
|
3244
|
-
applyAuthTokenFromElement(mutation.target as Element);
|
|
3245
|
-
}
|
|
3246
|
-
}
|
|
3247
|
-
};
|
|
3248
|
-
const observer = new MutationObserver(callback);
|
|
3249
|
-
observer.observe(targetNode, config);
|
|
3250
|
-
|
|
3251
|
-
return () => {
|
|
3252
|
-
observer.disconnect();
|
|
3253
|
-
};
|
|
3254
|
-
}, [memori.name, memori.memoriID, additionalInfo?.loginToken, authToken]);
|
|
3255
|
-
|
|
3256
|
-
/**
|
|
3257
|
-
* Experts references
|
|
3258
|
-
*/
|
|
3259
|
-
const [experts, setExperts] = useState<ExpertReference[]>();
|
|
3260
|
-
const fetchExperts = useCallback(async () => {
|
|
3261
|
-
if (!sessionId || !memori?.enableBoardOfExperts) return;
|
|
3262
|
-
|
|
3263
|
-
try {
|
|
3264
|
-
const { experts, count, ...resp } = await getExpertReferences(sessionId);
|
|
3265
|
-
|
|
3266
|
-
if (resp.resultCode === 0) {
|
|
3267
|
-
setExperts(experts);
|
|
3268
|
-
}
|
|
3269
|
-
} catch {
|
|
3270
|
-
// ignore expert fetch errors
|
|
3271
|
-
}
|
|
3272
|
-
}, [sessionId, memori?.enableBoardOfExperts]);
|
|
3273
|
-
useEffect(() => {
|
|
3274
|
-
fetchExperts();
|
|
3275
|
-
}, [sessionId, fetchExperts]);
|
|
3276
|
-
|
|
3277
|
-
const deepThoughtEnabled =
|
|
3278
|
-
memori.enableDeepThought &&
|
|
3279
|
-
!!loginToken &&
|
|
3280
|
-
!!user?.userID &&
|
|
3281
|
-
user?.pAndCUAccepted;
|
|
3282
|
-
|
|
3283
|
-
const handleNotEnoughCredits = useCallback(() => {
|
|
3284
|
-
setHasEnoughCredits(false);
|
|
3285
|
-
setAuthModalState(null);
|
|
3286
|
-
toast.error(t('notEnoughCredits'));
|
|
3287
|
-
}, [t]);
|
|
3288
|
-
const checkCredits = useCallback(
|
|
3289
|
-
async (options?: { notify?: boolean }) => {
|
|
3290
|
-
if (!tenant?.billingDelegation) return true;
|
|
3291
|
-
|
|
3292
|
-
// Billing delegation is active: credits MUST be verified.
|
|
3293
|
-
// Without either owner identifier we cannot call the API, so we fail closed
|
|
3294
|
-
// instead of silently letting the session start unverified.
|
|
3295
|
-
if (!ownerUserID && !ownerUserName) {
|
|
3296
|
-
if (options?.notify) {
|
|
3297
|
-
handleNotEnoughCredits();
|
|
3298
|
-
} else {
|
|
3299
|
-
setHasEnoughCredits(false);
|
|
3300
|
-
}
|
|
3301
|
-
return false;
|
|
3302
|
-
}
|
|
3303
|
-
|
|
3304
|
-
try {
|
|
3305
|
-
const resp = await getCredits({
|
|
3306
|
-
operation: deepThoughtEnabled
|
|
3307
|
-
? 'dt_session_creation'
|
|
3308
|
-
: 'session_creation',
|
|
3309
|
-
baseUrl: baseUrl,
|
|
3310
|
-
userID: ownerUserID,
|
|
3311
|
-
userName: ownerUserName,
|
|
3312
|
-
tenant: tenantID,
|
|
3313
|
-
});
|
|
3314
|
-
|
|
3315
|
-
if (resp.enough) {
|
|
3316
|
-
setHasEnoughCredits(true);
|
|
3317
|
-
return true;
|
|
3318
|
-
} else {
|
|
3319
|
-
if (options?.notify) {
|
|
3320
|
-
handleNotEnoughCredits();
|
|
3321
|
-
} else {
|
|
3322
|
-
setHasEnoughCredits(false);
|
|
3323
|
-
}
|
|
3324
|
-
return false;
|
|
3325
|
-
}
|
|
3326
|
-
} catch (err) {
|
|
3327
|
-
logWidgetError('checkCredits failed', err);
|
|
3328
|
-
return true;
|
|
3329
|
-
}
|
|
3330
|
-
},
|
|
3331
|
-
[
|
|
3332
|
-
baseUrl,
|
|
3333
|
-
deepThoughtEnabled,
|
|
3334
|
-
handleNotEnoughCredits,
|
|
3335
|
-
ownerUserID,
|
|
3336
|
-
ownerUserName,
|
|
3337
|
-
tenant?.billingDelegation,
|
|
3338
|
-
tenantID,
|
|
3339
|
-
]
|
|
3340
|
-
);
|
|
3341
|
-
useEffect(() => {
|
|
3342
|
-
if (tenant?.billingDelegation) {
|
|
3343
|
-
checkCredits();
|
|
3344
|
-
}
|
|
3345
|
-
}, [tenant?.billingDelegation, deepThoughtEnabled, checkCredits]);
|
|
3346
|
-
|
|
3347
|
-
useEffect(() => {
|
|
3348
|
-
if (__WEBCOMPONENT__) return;
|
|
3349
|
-
|
|
3350
|
-
const closeSession = () => {
|
|
3351
|
-
if (sessionId) {
|
|
3352
|
-
deleteSession(sessionId);
|
|
3353
|
-
}
|
|
3354
|
-
};
|
|
3355
|
-
|
|
3356
|
-
// delete session when the user closes the browser tab
|
|
3357
|
-
window.addEventListener('beforeunload', closeSession);
|
|
3358
|
-
|
|
3359
|
-
return () => {
|
|
3360
|
-
window.removeEventListener('beforeunload', closeSession);
|
|
3361
|
-
closeSession();
|
|
3362
|
-
};
|
|
3363
|
-
}, [sessionId]);
|
|
3364
|
-
|
|
3365
|
-
const showFullHistory =
|
|
3366
|
-
showOnlyLastMessages === undefined
|
|
3367
|
-
? selectedLayout !== 'TOTEM' && selectedLayout !== 'WEBSITE_ASSISTANT'
|
|
3368
|
-
: !showOnlyLastMessages;
|
|
3369
|
-
const canShowLoginButton =
|
|
3370
|
-
!tenant?.ssoLogin &&
|
|
3371
|
-
(showLogin ?? integrationConfig?.showLogin ?? memori.requireLoginToken);
|
|
3372
|
-
|
|
3373
|
-
const headerProps: HeaderProps = {
|
|
3374
|
-
memori: {
|
|
3375
|
-
...memori,
|
|
3376
|
-
ownerUserID: memori.ownerUserID ?? ownerUserID ?? undefined,
|
|
3377
|
-
},
|
|
3378
|
-
apiClient: client,
|
|
3379
|
-
tenant,
|
|
3380
|
-
history,
|
|
3381
|
-
showShare: showShare ?? integrationConfig?.showShare ?? true,
|
|
3382
|
-
position,
|
|
3383
|
-
layout: selectedLayout,
|
|
3384
|
-
additionalSettings,
|
|
3385
|
-
setShowPositionDrawer,
|
|
3386
|
-
setShowSettingsDrawer,
|
|
3387
|
-
setShowKnownFactsDrawer,
|
|
3388
|
-
setShowExpertsDrawer,
|
|
3389
|
-
enableAudio: enableAudio ?? integrationConfig?.enableAudio ?? true,
|
|
3390
|
-
speakerMuted: speakerMuted ?? false,
|
|
3391
|
-
setSpeakerMuted: (mute: boolean) => {
|
|
3392
|
-
toggleMute(mute);
|
|
3393
|
-
},
|
|
3394
|
-
setShowChatHistoryDrawer,
|
|
3395
|
-
showSettings: showSettings ?? integrationConfig?.showSettings ?? true,
|
|
3396
|
-
showChatHistory:
|
|
3397
|
-
showChatHistory ?? integrationConfig?.showChatHistory ?? true,
|
|
3398
|
-
showMessageConsumption: enableMessageConsumption,
|
|
3399
|
-
hasUserActivatedSpeak,
|
|
3400
|
-
showReload: selectedLayout === 'TOTEM',
|
|
3401
|
-
showClear: showClear ?? integrationConfig?.showClear ?? false,
|
|
3402
|
-
clearHistory: () => setHistory(h => h.slice(-1)),
|
|
3403
|
-
showLogin: canShowLoginButton,
|
|
3404
|
-
setShowLoginDrawer,
|
|
3405
|
-
loginToken,
|
|
3406
|
-
user,
|
|
3407
|
-
sessionID: sessionId,
|
|
3408
|
-
baseUrl,
|
|
3409
|
-
onLogout: () => {
|
|
3410
|
-
if (!loginToken) return;
|
|
3411
|
-
|
|
3412
|
-
client.backend.pwlUserLogout(loginToken).then(() => {
|
|
3413
|
-
setShowLoginDrawer(false);
|
|
3414
|
-
setUser(undefined);
|
|
3415
|
-
setLoginToken(undefined);
|
|
3416
|
-
userTokenRef.current = undefined;
|
|
3417
|
-
removeLocalConfig('loginToken');
|
|
3418
|
-
});
|
|
3419
|
-
},
|
|
3420
|
-
};
|
|
3421
|
-
|
|
3422
|
-
const avatarProps: AvatarProps = {
|
|
3423
|
-
memori,
|
|
3424
|
-
integration,
|
|
3425
|
-
integrationConfig,
|
|
3426
|
-
tenant,
|
|
3427
|
-
instruct,
|
|
3428
|
-
avatar3dVisible,
|
|
3429
|
-
setAvatar3dVisible,
|
|
3430
|
-
hasUserActivatedSpeak,
|
|
3431
|
-
isPlayingAudio:
|
|
3432
|
-
isPlayingAudio &&
|
|
3433
|
-
!speakerMuted &&
|
|
3434
|
-
(enableAudio ?? integrationConfig?.enableAudio ?? true),
|
|
3435
|
-
loading: !!memoriTyping,
|
|
3436
|
-
baseUrl,
|
|
3437
|
-
apiUrl: client.constants.BACKEND_URL,
|
|
3438
|
-
enablePositionControls,
|
|
3439
|
-
setEnablePositionControls,
|
|
3440
|
-
avatarType,
|
|
3441
|
-
};
|
|
3442
|
-
|
|
3443
|
-
const startPanelProps: StartPanelProps = {
|
|
3444
|
-
memori,
|
|
3445
|
-
tenant: tenant,
|
|
3446
|
-
language: language,
|
|
3447
|
-
userLang: userLang,
|
|
3448
|
-
setUserLang: setUserLang,
|
|
3449
|
-
baseUrl: baseUrl,
|
|
3450
|
-
apiUrl: client.constants.BACKEND_URL,
|
|
3451
|
-
position: position,
|
|
3452
|
-
openPositionDrawer: () => setShowPositionDrawer(true),
|
|
3453
|
-
integrationConfig: integrationConfig,
|
|
3454
|
-
instruct: instruct,
|
|
3455
|
-
sessionId: sessionId,
|
|
3456
|
-
clickedStart: clickedStart,
|
|
3457
|
-
isMultilanguageEnabled: isMultilanguageEnabled,
|
|
3458
|
-
onClickStart: onClickStart,
|
|
3459
|
-
isUserLoggedIn: !!loginToken && !!user?.userID,
|
|
3460
|
-
hasInitialSession: !!initialSessionID,
|
|
3461
|
-
notEnoughCredits: needsCredits && !hasEnoughCredits,
|
|
3462
|
-
showLogin: canShowLoginButton,
|
|
3463
|
-
setShowLoginDrawer,
|
|
3464
|
-
user,
|
|
3465
|
-
};
|
|
3466
|
-
|
|
3467
|
-
const chatProps: ChatProps = {
|
|
3468
|
-
memori,
|
|
3469
|
-
sessionID: sessionId || '',
|
|
3470
|
-
tenant,
|
|
3471
|
-
translateTo:
|
|
3472
|
-
isMultilanguageEnabled &&
|
|
3473
|
-
userLang.toUpperCase() !==
|
|
3474
|
-
(
|
|
3475
|
-
memori.culture?.split('-')?.[0] ??
|
|
3476
|
-
i18n.language ??
|
|
3477
|
-
'IT'
|
|
3478
|
-
)?.toUpperCase()
|
|
3479
|
-
? userLang
|
|
3480
|
-
: undefined,
|
|
3481
|
-
baseUrl,
|
|
3482
|
-
apiUrl: client.constants.BACKEND_URL,
|
|
3483
|
-
layout,
|
|
3484
|
-
memoriTyping,
|
|
3485
|
-
typingText,
|
|
3486
|
-
showTypingText:
|
|
3487
|
-
showTypingText ?? integrationConfig?.showTypingText ?? false,
|
|
3488
|
-
history: showFullHistory ? history : history.slice(-2),
|
|
3489
|
-
authToken: resolveLoginToken(),
|
|
3490
|
-
dialogState: currentDialogState,
|
|
3491
|
-
pushMessage,
|
|
3492
|
-
simulateUserPrompt,
|
|
3493
|
-
showDates,
|
|
3494
|
-
showContextPerLine,
|
|
3495
|
-
showMessageConsumption: enableMessageConsumption,
|
|
3496
|
-
showAIicon,
|
|
3497
|
-
showUpload: enableUpload,
|
|
3498
|
-
showReasoning: enableReasoning,
|
|
3499
|
-
showWhyThisAnswer,
|
|
3500
|
-
showCopyButton: showCopyButton ?? integrationConfig?.showCopyButton ?? true,
|
|
3501
|
-
showTranslationOriginal:
|
|
3502
|
-
showTranslationOriginal ??
|
|
3503
|
-
integrationConfig?.showTranslationOriginal ??
|
|
3504
|
-
false,
|
|
3505
|
-
client,
|
|
3506
|
-
instruct,
|
|
3507
|
-
preview,
|
|
3508
|
-
sendOnEnter,
|
|
3509
|
-
setSendOnEnter,
|
|
3510
|
-
microphoneMode: continuousSpeech ? 'CONTINUOUS' : 'HOLD_TO_TALK',
|
|
3511
|
-
attachmentsMenuOpen,
|
|
3512
|
-
setAttachmentsMenuOpen,
|
|
3513
|
-
showInputs,
|
|
3514
|
-
showMicrophone:
|
|
3515
|
-
!!ttsProvider && (enableAudio ?? integrationConfig?.enableAudio ?? true),
|
|
3516
|
-
showFunctionCache,
|
|
3517
|
-
userMessage,
|
|
3518
|
-
onChangeUserMessage,
|
|
3519
|
-
sendMessage: (msg: string, media?: (Medium & { type: string })[]) => {
|
|
3520
|
-
ttsStop();
|
|
3521
|
-
stopRecording();
|
|
3522
|
-
setHasUserTypedMessage(true); // Mark that user has typed a message
|
|
3523
|
-
sendMessage(msg, media);
|
|
3524
|
-
setUserMessage('');
|
|
3525
|
-
},
|
|
3526
|
-
stopListening: stopRecording,
|
|
3527
|
-
startListening: () => {
|
|
3528
|
-
setHasUserTypedMessage(false); // Reset typing flag when user starts listening
|
|
3529
|
-
startRecording();
|
|
3530
|
-
},
|
|
3531
|
-
stopAudio: ttsStop,
|
|
3532
|
-
listening: isListening,
|
|
3533
|
-
setEnableFocusChatInput,
|
|
3534
|
-
isPlayingAudio,
|
|
3535
|
-
customMediaRenderer,
|
|
3536
|
-
user,
|
|
3537
|
-
userAvatar,
|
|
3538
|
-
experts,
|
|
3539
|
-
useMathFormatting: applyMathFormatting,
|
|
3540
|
-
maxTotalMessagePayload,
|
|
3541
|
-
maxTextareaCharacters,
|
|
3542
|
-
};
|
|
3543
|
-
|
|
3544
|
-
const integrationBackground =
|
|
3545
|
-
integration && globalBackgroundUrl ? (
|
|
3546
|
-
<div className="memori--global-background">
|
|
3547
|
-
<div
|
|
3548
|
-
className="memori--global-background-image"
|
|
3549
|
-
style={{ backgroundImage: globalBackgroundUrl }}
|
|
3550
|
-
/>
|
|
3551
|
-
</div>
|
|
3552
|
-
) : (
|
|
3553
|
-
<div className="memori--global-background no-background-image" />
|
|
3554
|
-
);
|
|
3555
|
-
|
|
3556
|
-
const integrationStyle = integration ? (
|
|
3557
|
-
<style dangerouslySetInnerHTML={{ __html: integrationStylesheet }} />
|
|
3558
|
-
) : null;
|
|
3559
|
-
|
|
3560
|
-
const poweredBy = (
|
|
3561
|
-
<PoweredBy
|
|
3562
|
-
tenant={tenant}
|
|
3563
|
-
userLang={userLang}
|
|
3564
|
-
integrationID={integration?.integrationID}
|
|
3565
|
-
memoriHash={`${memori.ownerTenantName}-${memori.ownerUserName}-${memori.name}`}
|
|
3566
|
-
/>
|
|
3567
|
-
);
|
|
3568
|
-
|
|
3569
|
-
const Layout = customLayout
|
|
3570
|
-
? customLayout
|
|
3571
|
-
: selectedLayout === 'TOTEM'
|
|
3572
|
-
? TotemLayout
|
|
3573
|
-
: selectedLayout === 'CHAT'
|
|
3574
|
-
? ChatLayout
|
|
3575
|
-
: selectedLayout === 'FULLPAGE'
|
|
3576
|
-
? FullPageLayout
|
|
3577
|
-
: selectedLayout === 'WEBSITE_ASSISTANT'
|
|
3578
|
-
? WebsiteAssistantLayout
|
|
3579
|
-
: selectedLayout === 'HIDDEN_CHAT'
|
|
3580
|
-
? HiddenChatLayout
|
|
3581
|
-
: selectedLayout === 'ZOOMED_FULL_BODY'
|
|
3582
|
-
? ZoomedFullBodyLayout
|
|
3583
|
-
: FullPageLayout;
|
|
3584
|
-
|
|
3585
|
-
return (
|
|
3586
|
-
<div
|
|
3587
|
-
className={cx(
|
|
3588
|
-
'memori',
|
|
3589
|
-
'memori-widget',
|
|
3590
|
-
`memori-layout-${selectedLayout.toLowerCase()}`,
|
|
3591
|
-
`memori-controls-${controlsPosition.toLowerCase()}`,
|
|
3592
|
-
`memori--avatar-${integrationConfig?.avatar || 'default'}`,
|
|
3593
|
-
{
|
|
3594
|
-
'memori--auto-start': autoStart,
|
|
3595
|
-
'memori--preview': preview,
|
|
3596
|
-
'memori--embed': embed,
|
|
3597
|
-
'memori--with-integration': integration,
|
|
3598
|
-
'memori--with-speechkey': !!ttsProvider,
|
|
3599
|
-
'memori--active': hasUserActivatedSpeak,
|
|
3600
|
-
'memori--hide-emissions': hideEmissions,
|
|
3601
|
-
'memori--has-active-session': !!sessionId,
|
|
3602
|
-
}
|
|
3603
|
-
)}
|
|
3604
|
-
data-memori-name={memori?.name}
|
|
3605
|
-
data-memori-id={memori?.engineMemoriID}
|
|
3606
|
-
data-memori-secondary-id={memori?.memoriID}
|
|
3607
|
-
data-memori-session-id={sessionId}
|
|
3608
|
-
data-memori-integration={integration?.integrationID}
|
|
3609
|
-
data-memori-engine-state={JSON.stringify({
|
|
3610
|
-
...currentDialogState,
|
|
3611
|
-
sessionID: sessionId,
|
|
3612
|
-
})}
|
|
3613
|
-
style={{ height }}
|
|
3614
|
-
>
|
|
3615
|
-
<Layout
|
|
3616
|
-
Header={Header}
|
|
3617
|
-
headerProps={headerProps}
|
|
3618
|
-
Avatar={Avatar}
|
|
3619
|
-
avatarProps={avatarProps}
|
|
3620
|
-
Chat={Chat}
|
|
3621
|
-
chatProps={chatProps}
|
|
3622
|
-
StartPanel={StartPanel}
|
|
3623
|
-
startPanelProps={startPanelProps}
|
|
3624
|
-
integrationStyle={integrationStyle}
|
|
3625
|
-
integrationBackground={integrationBackground}
|
|
3626
|
-
poweredBy={poweredBy}
|
|
3627
|
-
autoStart={autoStart}
|
|
3628
|
-
sessionId={sessionId}
|
|
3629
|
-
hasUserActivatedSpeak={hasUserActivatedSpeak}
|
|
3630
|
-
loading={loading}
|
|
3631
|
-
avatar3dHidden={avatar3dHidden ?? integrationConfig?.avatar_3d_hidden}
|
|
3632
|
-
/>
|
|
3633
|
-
|
|
3634
|
-
<ArtifactAPIBridge
|
|
3635
|
-
pushMessage={(message: Message) => {
|
|
3636
|
-
setHistory(history => {
|
|
3637
|
-
if (!history.length) return history;
|
|
3638
|
-
const lastMessage = history[history.length - 1];
|
|
3639
|
-
if (!lastMessage || lastMessage.fromUser) return history;
|
|
3640
|
-
// Create a new message object with the updated text
|
|
3641
|
-
const updatedLastMessage = {
|
|
3642
|
-
...lastMessage,
|
|
3643
|
-
text: lastMessage.text + message.text,
|
|
3644
|
-
};
|
|
3645
|
-
return [...history.slice(0, -1), updatedLastMessage];
|
|
3646
|
-
});
|
|
3647
|
-
}}
|
|
3648
|
-
/>
|
|
3649
|
-
|
|
3650
|
-
<audio
|
|
3651
|
-
id="memori-audio"
|
|
3652
|
-
style={{ display: 'none' }}
|
|
3653
|
-
src="https://aisuru.com/intro.mp3"
|
|
3654
|
-
/>
|
|
3655
|
-
|
|
3656
|
-
{isClient && (
|
|
3657
|
-
<MemoriAuth
|
|
3658
|
-
withModal
|
|
3659
|
-
pwdOrTokens={authModalState}
|
|
3660
|
-
openModal={!!authModalState}
|
|
3661
|
-
setPwdOrTokens={setAuthModalState}
|
|
3662
|
-
showTokens={memori.privacyType === 'SECRET'}
|
|
3663
|
-
onFinish={(values: any) => {
|
|
3664
|
-
if (values['password']) setMemoriPwd(values['password']);
|
|
3665
|
-
if (values['password']) memoriPassword = values['password'];
|
|
3666
|
-
if (values['tokens']) setMemoriTokens(values['tokens']);
|
|
3667
|
-
|
|
3668
|
-
return reopenSession(
|
|
3669
|
-
!sessionId,
|
|
3670
|
-
values['password'],
|
|
3671
|
-
values['tokens'],
|
|
3672
|
-
personification?.tag,
|
|
3673
|
-
personification?.pin,
|
|
3674
|
-
{
|
|
3675
|
-
LANG: userLang,
|
|
3676
|
-
PATHNAME: window.location.pathname?.toUpperCase(),
|
|
3677
|
-
ROUTE:
|
|
3678
|
-
window.location.pathname?.split('/')?.pop()?.toUpperCase() ||
|
|
3679
|
-
'',
|
|
3680
|
-
...(initialContextVars || {}),
|
|
3681
|
-
},
|
|
3682
|
-
initialQuestion,
|
|
3683
|
-
birthDate
|
|
3684
|
-
)
|
|
3685
|
-
.then(state => {
|
|
3686
|
-
if (!state?.sessionID) {
|
|
3687
|
-
throw new Error('AUTH_FAILED');
|
|
3688
|
-
}
|
|
3689
|
-
|
|
3690
|
-
setAuthModalState(null);
|
|
3691
|
-
// If we got a valid state from reopenSession, don't call onClickStart again
|
|
3692
|
-
// to avoid duplicate snippet execution
|
|
3693
|
-
if (state?.dialogState) {
|
|
3694
|
-
setHasUserActivatedSpeak(true);
|
|
3695
|
-
} else {
|
|
3696
|
-
// Only call onClickStart if reopenSession didn't return a valid state
|
|
3697
|
-
onClickStart(state);
|
|
3698
|
-
}
|
|
3699
|
-
})
|
|
3700
|
-
.catch(error => {
|
|
3701
|
-
throw error;
|
|
3702
|
-
});
|
|
3703
|
-
}}
|
|
3704
|
-
minimumNumberOfRecoveryTokens={
|
|
3705
|
-
memori?.minimumNumberOfRecoveryTokens ?? 1
|
|
3706
|
-
}
|
|
3707
|
-
/>
|
|
3708
|
-
)}
|
|
3709
|
-
|
|
3710
|
-
{isClient && (
|
|
3711
|
-
<AgeVerificationModal
|
|
3712
|
-
visible={showAgeVerification}
|
|
3713
|
-
minAge={minAge}
|
|
3714
|
-
onClose={birthDate => {
|
|
3715
|
-
if (birthDate) {
|
|
3716
|
-
setBirthDate(birthDate);
|
|
3717
|
-
|
|
3718
|
-
setLocalConfig('birthDate', birthDate);
|
|
3719
|
-
|
|
3720
|
-
reopenSession(
|
|
3721
|
-
!sessionId,
|
|
3722
|
-
memoriPassword || memoriPwd || memori?.secretToken,
|
|
3723
|
-
memoriTokens,
|
|
3724
|
-
personification?.tag,
|
|
3725
|
-
personification?.pin,
|
|
3726
|
-
{
|
|
3727
|
-
LANG: userLang,
|
|
3728
|
-
PATHNAME: window.location.pathname?.toUpperCase(),
|
|
3729
|
-
ROUTE:
|
|
3730
|
-
window.location.pathname
|
|
3731
|
-
?.split('/')
|
|
3732
|
-
?.pop()
|
|
3733
|
-
?.toUpperCase() || '',
|
|
3734
|
-
...(initialContextVars || {}),
|
|
3735
|
-
},
|
|
3736
|
-
initialQuestion,
|
|
3737
|
-
birthDate
|
|
3738
|
-
)
|
|
3739
|
-
.then(state => {
|
|
3740
|
-
setShowAgeVerification(false);
|
|
3741
|
-
setAuthModalState(null);
|
|
3742
|
-
onClickStart(state || undefined);
|
|
3743
|
-
})
|
|
3744
|
-
.catch(() => {
|
|
3745
|
-
setShowAgeVerification(false);
|
|
3746
|
-
});
|
|
3747
|
-
} else {
|
|
3748
|
-
setShowAgeVerification(false);
|
|
3749
|
-
setClickedStart(false);
|
|
3750
|
-
}
|
|
3751
|
-
}}
|
|
3752
|
-
/>
|
|
3753
|
-
)}
|
|
3754
|
-
|
|
3755
|
-
{showSettingsDrawer && (
|
|
3756
|
-
<SettingsDrawer
|
|
3757
|
-
layout={selectedLayout}
|
|
3758
|
-
open={!!showSettingsDrawer}
|
|
3759
|
-
onClose={() => setShowSettingsDrawer(false)}
|
|
3760
|
-
microphoneMode={continuousSpeech ? 'CONTINUOUS' : 'HOLD_TO_TALK'}
|
|
3761
|
-
continuousSpeechTimeout={continuousSpeechTimeout}
|
|
3762
|
-
setMicrophoneMode={mode => setContinuousSpeech(mode === 'CONTINUOUS')}
|
|
3763
|
-
setContinuousSpeechTimeout={setContinuousSpeechTimeout}
|
|
3764
|
-
controlsPosition={controlsPosition}
|
|
3765
|
-
setControlsPosition={setControlsPosition}
|
|
3766
|
-
hideEmissions={hideEmissions}
|
|
3767
|
-
setHideEmissions={setHideEmissions}
|
|
3768
|
-
avatarType={avatarType}
|
|
3769
|
-
setAvatarType={setAvatarType}
|
|
3770
|
-
enablePositionControls={enablePositionControls}
|
|
3771
|
-
setEnablePositionControls={setEnablePositionControls}
|
|
3772
|
-
isAvatar3d={!!integrationConfig?.avatarURL}
|
|
3773
|
-
additionalSettings={additionalSettings}
|
|
3774
|
-
speakerMuted={speakerMuted}
|
|
3775
|
-
/>
|
|
3776
|
-
)}
|
|
3777
|
-
|
|
3778
|
-
{showChatHistoryDrawer && (
|
|
3779
|
-
<ChatHistoryDrawer
|
|
3780
|
-
open={!!showChatHistoryDrawer}
|
|
3781
|
-
onClose={() => setShowChatHistoryDrawer(false)}
|
|
3782
|
-
resumeSession={chatLog => {
|
|
3783
|
-
setChatLogID(chatLog.chatLogID);
|
|
3784
|
-
onClickStart(undefined, false, chatLog);
|
|
3785
|
-
setShowChatHistoryDrawer(false);
|
|
3786
|
-
}}
|
|
3787
|
-
apiClient={client}
|
|
3788
|
-
sessionId={sessionId || ''}
|
|
3789
|
-
memori={memori}
|
|
3790
|
-
baseUrl={baseUrl}
|
|
3791
|
-
history={history}
|
|
3792
|
-
apiUrl={client.constants.BACKEND_URL}
|
|
3793
|
-
loginToken={loginToken}
|
|
3794
|
-
language={language}
|
|
3795
|
-
userLang={userLang}
|
|
3796
|
-
isMultilanguageEnabled={isMultilanguageEnabled}
|
|
3797
|
-
/>
|
|
3798
|
-
)}
|
|
3799
|
-
|
|
3800
|
-
{showPositionDrawer && (
|
|
3801
|
-
<PositionDrawer
|
|
3802
|
-
memori={memori}
|
|
3803
|
-
open={!!showPositionDrawer}
|
|
3804
|
-
venue={position}
|
|
3805
|
-
setVenue={setPosition}
|
|
3806
|
-
onClose={() => {
|
|
3807
|
-
setShowPositionDrawer(false);
|
|
3808
|
-
if (autoStart) {
|
|
3809
|
-
onClickStart();
|
|
3810
|
-
}
|
|
3811
|
-
}}
|
|
3812
|
-
drawerClassName={
|
|
3813
|
-
selectedLayout === 'WEBSITE_ASSISTANT'
|
|
3814
|
-
? 'memori-drawer--above-website-assistant'
|
|
3815
|
-
: undefined
|
|
3816
|
-
}
|
|
3817
|
-
/>
|
|
3818
|
-
)}
|
|
3819
|
-
|
|
3820
|
-
{showKnownFactsDrawer && sessionId && (
|
|
3821
|
-
<KnownFacts
|
|
3822
|
-
apiClient={client}
|
|
3823
|
-
memori={memori}
|
|
3824
|
-
sessionID={sessionId}
|
|
3825
|
-
visible={showKnownFactsDrawer}
|
|
3826
|
-
closeDrawer={() => setShowKnownFactsDrawer(false)}
|
|
3827
|
-
/>
|
|
3828
|
-
)}
|
|
3829
|
-
|
|
3830
|
-
{showExpertsDrawer && !!experts && (
|
|
3831
|
-
<ExpertsDrawer
|
|
3832
|
-
apiUrl={client.constants.BACKEND_URL}
|
|
3833
|
-
baseUrl={baseUrl}
|
|
3834
|
-
tenant={tenant}
|
|
3835
|
-
experts={experts}
|
|
3836
|
-
open={showExpertsDrawer}
|
|
3837
|
-
onClose={() => setShowExpertsDrawer(false)}
|
|
3838
|
-
/>
|
|
3839
|
-
)}
|
|
3840
|
-
|
|
3841
|
-
{showLoginDrawer && tenant?.name && (
|
|
3842
|
-
<LoginDrawer
|
|
3843
|
-
tenant={tenant}
|
|
3844
|
-
apiClient={client}
|
|
3845
|
-
open={!!showLoginDrawer}
|
|
3846
|
-
user={user}
|
|
3847
|
-
loginToken={loginToken}
|
|
3848
|
-
onClose={() => setShowLoginDrawer(false)}
|
|
3849
|
-
drawerClassName={
|
|
3850
|
-
selectedLayout === 'WEBSITE_ASSISTANT'
|
|
3851
|
-
? 'memori-drawer--above-website-assistant'
|
|
3852
|
-
: undefined
|
|
3853
|
-
}
|
|
3854
|
-
onLogin={(user, token) => {
|
|
3855
|
-
//The user is logged in, so we need to set open a new session with the new token
|
|
3856
|
-
reopenSession(
|
|
3857
|
-
false,
|
|
3858
|
-
memoriPassword || memoriPwd || memori?.secretToken,
|
|
3859
|
-
[],
|
|
3860
|
-
personification?.tag,
|
|
3861
|
-
personification?.pin,
|
|
3862
|
-
{
|
|
3863
|
-
LANG: userLang,
|
|
3864
|
-
PATHNAME: window.location.pathname?.toUpperCase(),
|
|
3865
|
-
ROUTE:
|
|
3866
|
-
window.location.pathname?.split('/')?.pop()?.toUpperCase() ||
|
|
3867
|
-
'',
|
|
3868
|
-
...(initialContextVars || {}),
|
|
3869
|
-
},
|
|
3870
|
-
undefined, // Don't send initialQuestion after login, only show the login status chip
|
|
3871
|
-
birthDate,
|
|
3872
|
-
{ loginToken: token } as any,
|
|
3873
|
-
undefined,
|
|
3874
|
-
sessionId
|
|
3875
|
-
).then(state => {
|
|
3876
|
-
setShowLoginDrawer(false);
|
|
3877
|
-
setUser(user);
|
|
3878
|
-
setLoginToken(token);
|
|
3879
|
-
userTokenRef.current = token;
|
|
3880
|
-
setLocalConfig('loginToken', token);
|
|
3881
|
-
// Push a message with initial status to show status message when a new session is created after login
|
|
3882
|
-
if (
|
|
3883
|
-
state?.sessionID &&
|
|
3884
|
-
state.sessionID !== sessionId &&
|
|
3885
|
-
state?.dialogState
|
|
3886
|
-
) {
|
|
3887
|
-
// Push a message with initial status message showing successful login
|
|
3888
|
-
// Only show the chip component, not the emission text
|
|
3889
|
-
const username = user?.userName || t('login.user');
|
|
3890
|
-
pushMessage({
|
|
3891
|
-
text: '', // Empty text so only the chip is visible
|
|
3892
|
-
emitter: state.dialogState.emitter,
|
|
3893
|
-
media:
|
|
3894
|
-
state.dialogState.emittedMedia ??
|
|
3895
|
-
state.dialogState.media ??
|
|
3896
|
-
[],
|
|
3897
|
-
fromUser: false,
|
|
3898
|
-
initial: t('login.successfullyLoggedIn', { username }) as any,
|
|
3899
|
-
contextVars: state.dialogState.contextVars,
|
|
3900
|
-
date: state.dialogState.currentDate,
|
|
3901
|
-
placeName: state.dialogState.currentPlaceName,
|
|
3902
|
-
placeLatitude: state.dialogState.currentLatitude,
|
|
3903
|
-
placeLongitude: state.dialogState.currentLongitude,
|
|
3904
|
-
placeUncertaintyKm: state.dialogState.currentUncertaintyKm,
|
|
3905
|
-
tag: state.dialogState.currentTag,
|
|
3906
|
-
memoryTags: state.dialogState.memoryTags,
|
|
3907
|
-
});
|
|
3908
|
-
// Update the dialog state so the UI reflects the new session
|
|
3909
|
-
setCurrentDialogState(state.dialogState);
|
|
3910
|
-
}
|
|
3911
|
-
});
|
|
3912
|
-
}}
|
|
3913
|
-
setUser={setUser}
|
|
3914
|
-
onLogout={() => {
|
|
3915
|
-
if (!loginToken) return;
|
|
3916
|
-
client.backend.pwlUserLogout(loginToken).then(() => {
|
|
3917
|
-
setShowLoginDrawer(false);
|
|
3918
|
-
setUser(undefined);
|
|
3919
|
-
setLoginToken(undefined);
|
|
3920
|
-
userTokenRef.current = undefined;
|
|
3921
|
-
removeLocalConfig('loginToken');
|
|
3922
|
-
});
|
|
3923
|
-
}}
|
|
3924
|
-
/>
|
|
3925
|
-
)}
|
|
3926
|
-
</div>
|
|
3927
|
-
);
|
|
3928
|
-
};
|
|
3929
|
-
|
|
3930
|
-
export default MemoriWidget;
|