@memori.ai/memori-react 8.45.2 → 8.45.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (729) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/components/Chat/Chat.d.ts +1 -0
  3. package/dist/components/Chat/Chat.js +6 -3
  4. package/dist/components/Chat/Chat.js.map +1 -1
  5. package/dist/components/ChatHistoryDrawer/ChatResumeDrawer.css +78 -0
  6. package/dist/components/ChatHistoryDrawer/ChatResumeDrawer.d.ts +39 -0
  7. package/dist/components/ChatHistoryDrawer/ChatResumeDrawer.js +43 -0
  8. package/dist/components/ChatHistoryDrawer/ChatResumeDrawer.js.map +1 -0
  9. package/dist/components/ChatInputs/ChatInputs.d.ts +1 -0
  10. package/dist/components/ChatInputs/ChatInputs.js +2 -2
  11. package/dist/components/ChatInputs/ChatInputs.js.map +1 -1
  12. package/dist/components/ContentPreviewModal/DocumentMarkdown.d.ts +7 -0
  13. package/dist/components/ContentPreviewModal/DocumentMarkdown.js +31 -0
  14. package/dist/components/ContentPreviewModal/DocumentMarkdown.js.map +1 -0
  15. package/dist/components/DrawerFooter/DrawerFooter.css +62 -0
  16. package/dist/components/DrawerFooter/DrawerFooter.d.ts +12 -0
  17. package/dist/components/DrawerFooter/DrawerFooter.js +10 -0
  18. package/dist/components/DrawerFooter/DrawerFooter.js.map +1 -0
  19. package/dist/components/IconButton/IconButton.css +135 -0
  20. package/dist/components/IconButton/IconButton.d.ts +9 -0
  21. package/dist/components/IconButton/IconButton.js +36 -0
  22. package/dist/components/IconButton/IconButton.js.map +1 -0
  23. package/dist/components/LoginModal/LoginModal.css +270 -0
  24. package/dist/components/LoginModal/LoginModal.d.ts +19 -0
  25. package/dist/components/LoginModal/LoginModal.js +355 -0
  26. package/dist/components/LoginModal/LoginModal.js.map +1 -0
  27. package/dist/components/MemoriWidget/MemoriWidget.d.ts +2 -2
  28. package/dist/components/MemoriWidget/MemoriWidget.js +13 -21
  29. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  30. package/dist/components/MobileSessionPanel/MobileSessionPanel.css +540 -0
  31. package/dist/components/MobileSessionPanel/MobileSessionPanel.d.ts +65 -0
  32. package/dist/components/MobileSessionPanel/MobileSessionPanel.js +254 -0
  33. package/dist/components/MobileSessionPanel/MobileSessionPanel.js.map +1 -0
  34. package/dist/components/PositionPopover/PositionPopover.css +218 -0
  35. package/dist/components/PositionPopover/PositionPopover.d.ts +22 -0
  36. package/dist/components/PositionPopover/PositionPopover.js +208 -0
  37. package/dist/components/PositionPopover/PositionPopover.js.map +1 -0
  38. package/dist/components/ReasoningBlock/ReasoningBlock.css +156 -0
  39. package/dist/components/ReasoningBlock/ReasoningBlock.d.ts +7 -0
  40. package/dist/components/ReasoningBlock/ReasoningBlock.js +61 -0
  41. package/dist/components/ReasoningBlock/ReasoningBlock.js.map +1 -0
  42. package/dist/components/SideDrawer/SideDrawer.css +112 -0
  43. package/dist/components/SideDrawer/SideDrawer.d.ts +22 -0
  44. package/dist/components/SideDrawer/SideDrawer.js +17 -0
  45. package/dist/components/SideDrawer/SideDrawer.js.map +1 -0
  46. package/dist/components/UploadButton/UploadButton.d.ts +2 -0
  47. package/dist/components/UploadButton/UploadButton.js +2 -2
  48. package/dist/components/UploadButton/UploadButton.js.map +1 -1
  49. package/dist/components/UploadButton/UploadImages/UploadImages.d.ts +2 -0
  50. package/dist/components/UploadButton/UploadImages/UploadImages.js +7 -1
  51. package/dist/components/UploadButton/UploadImages/UploadImages.js.map +1 -1
  52. package/dist/components/layouts/WebsiteAssistant/WebsiteAssistant.d.ts +4 -0
  53. package/dist/components/layouts/WebsiteAssistant/WebsiteAssistant.js +144 -0
  54. package/dist/components/layouts/WebsiteAssistant/WebsiteAssistant.js.map +1 -0
  55. package/{src/components/layouts → dist/components/layouts/WebsiteAssistant}/website-assistant.css +298 -86
  56. package/dist/components/layouts/chat.css +27 -4
  57. package/dist/components/layouts/fullpage.css +628 -0
  58. package/dist/helpers/artifactPanel.d.ts +6 -0
  59. package/dist/helpers/artifactPanel.js +42 -0
  60. package/dist/helpers/artifactPanel.js.map +1 -0
  61. package/dist/helpers/chatScroll.d.ts +12 -0
  62. package/dist/helpers/chatScroll.js +14 -0
  63. package/dist/helpers/chatScroll.js.map +1 -0
  64. package/dist/helpers/conversationHistory.d.ts +16 -0
  65. package/dist/helpers/conversationHistory.js +89 -0
  66. package/dist/helpers/conversationHistory.js.map +1 -0
  67. package/dist/helpers/conversationTitle.d.ts +8 -0
  68. package/dist/helpers/conversationTitle.js +27 -0
  69. package/dist/helpers/conversationTitle.js.map +1 -0
  70. package/dist/helpers/enhanceBubbleCodeBlocks.d.ts +5 -0
  71. package/dist/helpers/enhanceBubbleCodeBlocks.js +97 -0
  72. package/dist/helpers/enhanceBubbleCodeBlocks.js.map +1 -0
  73. package/dist/helpers/highlightMatches.d.ts +2 -0
  74. package/dist/helpers/highlightMatches.js +54 -0
  75. package/dist/helpers/highlightMatches.js.map +1 -0
  76. package/dist/helpers/nats/dialogStateCatchUp.d.ts +3 -0
  77. package/dist/helpers/nats/dialogStateCatchUp.js +40 -0
  78. package/dist/helpers/nats/dialogStateCatchUp.js.map +1 -0
  79. package/dist/helpers/positionPopover.d.ts +3 -0
  80. package/dist/helpers/positionPopover.js +22 -0
  81. package/dist/helpers/positionPopover.js.map +1 -0
  82. package/dist/helpers/prism.d.ts +16 -0
  83. package/dist/helpers/prism.js +78 -0
  84. package/dist/helpers/prism.js.map +1 -0
  85. package/dist/helpers/reasoning.d.ts +7 -0
  86. package/dist/helpers/reasoning.js +43 -0
  87. package/dist/helpers/reasoning.js.map +1 -0
  88. package/dist/helpers/relativeTime.d.ts +3 -0
  89. package/dist/helpers/relativeTime.js +56 -0
  90. package/dist/helpers/relativeTime.js.map +1 -0
  91. package/dist/helpers/sourcesCount.d.ts +2 -0
  92. package/dist/helpers/sourcesCount.js +22 -0
  93. package/dist/helpers/sourcesCount.js.map +1 -0
  94. package/dist/helpers/tts/muteSpeaker.d.ts +13 -0
  95. package/dist/helpers/tts/muteSpeaker.js +26 -0
  96. package/dist/helpers/tts/muteSpeaker.js.map +1 -0
  97. package/dist/helpers/usePressTooltip.d.ts +18 -0
  98. package/dist/helpers/usePressTooltip.js +38 -0
  99. package/dist/helpers/usePressTooltip.js.map +1 -0
  100. package/dist/icons/FacebookIcon.d.ts +3 -0
  101. package/dist/icons/FacebookIcon.js +6 -0
  102. package/dist/icons/FacebookIcon.js.map +1 -0
  103. package/dist/icons/LinkedinIcon.d.ts +3 -0
  104. package/dist/icons/LinkedinIcon.js +6 -0
  105. package/dist/icons/LinkedinIcon.js.map +1 -0
  106. package/dist/icons/MemoriIcon.d.ts +3 -0
  107. package/dist/icons/MemoriIcon.js +8 -0
  108. package/dist/icons/MemoriIcon.js.map +1 -0
  109. package/dist/icons/TelegramIcon.d.ts +3 -0
  110. package/dist/icons/TelegramIcon.js +6 -0
  111. package/dist/icons/TelegramIcon.js.map +1 -0
  112. package/dist/icons/TwitterIcon.d.ts +3 -0
  113. package/dist/icons/TwitterIcon.js +6 -0
  114. package/dist/icons/TwitterIcon.js.map +1 -0
  115. package/dist/icons/WhatsappIcon.d.ts +3 -0
  116. package/dist/icons/WhatsappIcon.js +6 -0
  117. package/dist/icons/WhatsappIcon.js.map +1 -0
  118. package/dist/icons/artifacts/DocumentIcon.d.ts +3 -0
  119. package/dist/icons/artifacts/DocumentIcon.js +6 -0
  120. package/dist/icons/artifacts/DocumentIcon.js.map +1 -0
  121. package/dist/icons/artifacts/HtmlIcon.d.ts +3 -0
  122. package/dist/icons/artifacts/HtmlIcon.js +6 -0
  123. package/dist/icons/artifacts/HtmlIcon.js.map +1 -0
  124. package/dist/icons/artifacts/JavascriptIcon.d.ts +3 -0
  125. package/dist/icons/artifacts/JavascriptIcon.js +6 -0
  126. package/dist/icons/artifacts/JavascriptIcon.js.map +1 -0
  127. package/dist/icons/artifacts/MarkdownIcon.d.ts +3 -0
  128. package/dist/icons/artifacts/MarkdownIcon.js +6 -0
  129. package/dist/icons/artifacts/MarkdownIcon.js.map +1 -0
  130. package/dist/icons/artifacts/PythonIcon.d.ts +3 -0
  131. package/dist/icons/artifacts/PythonIcon.js +6 -0
  132. package/dist/icons/artifacts/PythonIcon.js.map +1 -0
  133. package/dist/icons/artifacts/SvgIcon.d.ts +3 -0
  134. package/dist/icons/artifacts/SvgIcon.js +6 -0
  135. package/dist/icons/artifacts/SvgIcon.js.map +1 -0
  136. package/dist/icons/artifacts/XmlIcon.d.ts +3 -0
  137. package/dist/icons/artifacts/XmlIcon.js +6 -0
  138. package/dist/icons/artifacts/XmlIcon.js.map +1 -0
  139. package/dist/icons/artifacts/cssIcon.d.ts +3 -0
  140. package/dist/icons/artifacts/cssIcon.js +6 -0
  141. package/dist/icons/artifacts/cssIcon.js.map +1 -0
  142. package/dist/styles/motion.css +25 -0
  143. package/dist/styles.css +1 -2
  144. package/dist/version.d.ts +1 -1
  145. package/dist/version.js +1 -1
  146. package/esm/components/Chat/Chat.d.ts +1 -0
  147. package/esm/components/Chat/Chat.js +6 -3
  148. package/esm/components/Chat/Chat.js.map +1 -1
  149. package/esm/components/ChatHistoryDrawer/ChatResumeDrawer.d.ts +39 -0
  150. package/esm/components/ChatHistoryDrawer/ChatResumeDrawer.js +40 -0
  151. package/esm/components/ChatHistoryDrawer/ChatResumeDrawer.js.map +1 -0
  152. package/esm/components/ChatInputs/ChatInputs.d.ts +1 -0
  153. package/esm/components/ChatInputs/ChatInputs.js +2 -2
  154. package/esm/components/ChatInputs/ChatInputs.js.map +1 -1
  155. package/esm/components/ContentPreviewModal/DocumentMarkdown.d.ts +7 -0
  156. package/esm/components/ContentPreviewModal/DocumentMarkdown.js +28 -0
  157. package/esm/components/ContentPreviewModal/DocumentMarkdown.js.map +1 -0
  158. package/esm/components/DrawerFooter/DrawerFooter.d.ts +12 -0
  159. package/esm/components/DrawerFooter/DrawerFooter.js +8 -0
  160. package/esm/components/DrawerFooter/DrawerFooter.js.map +1 -0
  161. package/esm/components/IconButton/IconButton.d.ts +9 -0
  162. package/esm/components/IconButton/IconButton.js +33 -0
  163. package/esm/components/IconButton/IconButton.js.map +1 -0
  164. package/esm/components/LoginModal/LoginModal.d.ts +19 -0
  165. package/esm/components/LoginModal/LoginModal.js +352 -0
  166. package/esm/components/LoginModal/LoginModal.js.map +1 -0
  167. package/esm/components/MemoriWidget/MemoriWidget.d.ts +2 -2
  168. package/esm/components/MemoriWidget/MemoriWidget.js +8 -17
  169. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  170. package/esm/components/MobileSessionPanel/MobileSessionPanel.d.ts +65 -0
  171. package/esm/components/MobileSessionPanel/MobileSessionPanel.js +251 -0
  172. package/esm/components/MobileSessionPanel/MobileSessionPanel.js.map +1 -0
  173. package/esm/components/PositionPopover/PositionPopover.d.ts +22 -0
  174. package/esm/components/PositionPopover/PositionPopover.js +203 -0
  175. package/esm/components/PositionPopover/PositionPopover.js.map +1 -0
  176. package/esm/components/ReasoningBlock/ReasoningBlock.d.ts +7 -0
  177. package/esm/components/ReasoningBlock/ReasoningBlock.js +58 -0
  178. package/esm/components/ReasoningBlock/ReasoningBlock.js.map +1 -0
  179. package/esm/components/SideDrawer/SideDrawer.d.ts +22 -0
  180. package/esm/components/SideDrawer/SideDrawer.js +12 -0
  181. package/esm/components/SideDrawer/SideDrawer.js.map +1 -0
  182. package/esm/components/UploadButton/UploadButton.d.ts +2 -0
  183. package/esm/components/UploadButton/UploadButton.js +2 -2
  184. package/esm/components/UploadButton/UploadButton.js.map +1 -1
  185. package/esm/components/UploadButton/UploadImages/UploadImages.d.ts +2 -0
  186. package/esm/components/UploadButton/UploadImages/UploadImages.js +7 -1
  187. package/esm/components/UploadButton/UploadImages/UploadImages.js.map +1 -1
  188. package/esm/components/layouts/WebsiteAssistant/WebsiteAssistant.d.ts +4 -0
  189. package/esm/components/layouts/WebsiteAssistant/WebsiteAssistant.js +141 -0
  190. package/esm/components/layouts/WebsiteAssistant/WebsiteAssistant.js.map +1 -0
  191. package/esm/components/layouts/chat.css +27 -4
  192. package/esm/helpers/artifactPanel.d.ts +6 -0
  193. package/esm/helpers/artifactPanel.js +37 -0
  194. package/esm/helpers/artifactPanel.js.map +1 -0
  195. package/esm/helpers/chatScroll.d.ts +12 -0
  196. package/esm/helpers/chatScroll.js +9 -0
  197. package/esm/helpers/chatScroll.js.map +1 -0
  198. package/esm/helpers/conversationHistory.d.ts +16 -0
  199. package/esm/helpers/conversationHistory.js +82 -0
  200. package/esm/helpers/conversationHistory.js.map +1 -0
  201. package/esm/helpers/conversationTitle.d.ts +8 -0
  202. package/esm/helpers/conversationTitle.js +23 -0
  203. package/esm/helpers/conversationTitle.js.map +1 -0
  204. package/esm/helpers/enhanceBubbleCodeBlocks.d.ts +5 -0
  205. package/esm/helpers/enhanceBubbleCodeBlocks.js +94 -0
  206. package/esm/helpers/enhanceBubbleCodeBlocks.js.map +1 -0
  207. package/esm/helpers/highlightMatches.d.ts +2 -0
  208. package/esm/helpers/highlightMatches.js +49 -0
  209. package/esm/helpers/highlightMatches.js.map +1 -0
  210. package/esm/helpers/nats/dialogStateCatchUp.d.ts +3 -0
  211. package/esm/helpers/nats/dialogStateCatchUp.js +35 -0
  212. package/esm/helpers/nats/dialogStateCatchUp.js.map +1 -0
  213. package/esm/helpers/positionPopover.d.ts +3 -0
  214. package/esm/helpers/positionPopover.js +17 -0
  215. package/esm/helpers/positionPopover.js.map +1 -0
  216. package/esm/helpers/prism.d.ts +16 -0
  217. package/esm/helpers/prism.js +74 -0
  218. package/esm/helpers/prism.js.map +1 -0
  219. package/esm/helpers/reasoning.d.ts +7 -0
  220. package/esm/helpers/reasoning.js +39 -0
  221. package/esm/helpers/reasoning.js.map +1 -0
  222. package/esm/helpers/relativeTime.d.ts +3 -0
  223. package/esm/helpers/relativeTime.js +50 -0
  224. package/esm/helpers/relativeTime.js.map +1 -0
  225. package/esm/helpers/sourcesCount.d.ts +2 -0
  226. package/esm/helpers/sourcesCount.js +18 -0
  227. package/esm/helpers/sourcesCount.js.map +1 -0
  228. package/esm/helpers/tts/muteSpeaker.d.ts +13 -0
  229. package/esm/helpers/tts/muteSpeaker.js +21 -0
  230. package/esm/helpers/tts/muteSpeaker.js.map +1 -0
  231. package/esm/helpers/usePressTooltip.d.ts +18 -0
  232. package/esm/helpers/usePressTooltip.js +35 -0
  233. package/esm/helpers/usePressTooltip.js.map +1 -0
  234. package/esm/icons/FacebookIcon.d.ts +3 -0
  235. package/esm/icons/FacebookIcon.js +4 -0
  236. package/esm/icons/FacebookIcon.js.map +1 -0
  237. package/esm/icons/LinkedinIcon.d.ts +3 -0
  238. package/esm/icons/LinkedinIcon.js +4 -0
  239. package/esm/icons/LinkedinIcon.js.map +1 -0
  240. package/esm/icons/MemoriIcon.d.ts +3 -0
  241. package/esm/icons/MemoriIcon.js +6 -0
  242. package/esm/icons/MemoriIcon.js.map +1 -0
  243. package/esm/icons/TelegramIcon.d.ts +3 -0
  244. package/esm/icons/TelegramIcon.js +4 -0
  245. package/esm/icons/TelegramIcon.js.map +1 -0
  246. package/esm/icons/TwitterIcon.d.ts +3 -0
  247. package/esm/icons/TwitterIcon.js +4 -0
  248. package/esm/icons/TwitterIcon.js.map +1 -0
  249. package/esm/icons/WhatsappIcon.d.ts +3 -0
  250. package/esm/icons/WhatsappIcon.js +4 -0
  251. package/esm/icons/WhatsappIcon.js.map +1 -0
  252. package/esm/icons/artifacts/DocumentIcon.d.ts +3 -0
  253. package/esm/icons/artifacts/DocumentIcon.js +4 -0
  254. package/esm/icons/artifacts/DocumentIcon.js.map +1 -0
  255. package/esm/icons/artifacts/HtmlIcon.d.ts +3 -0
  256. package/esm/icons/artifacts/HtmlIcon.js +4 -0
  257. package/esm/icons/artifacts/HtmlIcon.js.map +1 -0
  258. package/esm/icons/artifacts/JavascriptIcon.d.ts +3 -0
  259. package/esm/icons/artifacts/JavascriptIcon.js +4 -0
  260. package/esm/icons/artifacts/JavascriptIcon.js.map +1 -0
  261. package/esm/icons/artifacts/MarkdownIcon.d.ts +3 -0
  262. package/esm/icons/artifacts/MarkdownIcon.js +4 -0
  263. package/esm/icons/artifacts/MarkdownIcon.js.map +1 -0
  264. package/esm/icons/artifacts/PythonIcon.d.ts +3 -0
  265. package/esm/icons/artifacts/PythonIcon.js +4 -0
  266. package/esm/icons/artifacts/PythonIcon.js.map +1 -0
  267. package/esm/icons/artifacts/SvgIcon.d.ts +3 -0
  268. package/esm/icons/artifacts/SvgIcon.js +4 -0
  269. package/esm/icons/artifacts/SvgIcon.js.map +1 -0
  270. package/esm/icons/artifacts/XmlIcon.d.ts +3 -0
  271. package/esm/icons/artifacts/XmlIcon.js +4 -0
  272. package/esm/icons/artifacts/XmlIcon.js.map +1 -0
  273. package/esm/icons/artifacts/cssIcon.d.ts +3 -0
  274. package/esm/icons/artifacts/cssIcon.js +4 -0
  275. package/esm/icons/artifacts/cssIcon.js.map +1 -0
  276. package/esm/styles.css +1 -2
  277. package/esm/version.d.ts +1 -1
  278. package/esm/version.js +1 -1
  279. package/package.json +7 -7
  280. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.d.ts +0 -3
  281. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.js +0 -64
  282. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.js.map +0 -1
  283. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.d.ts +0 -3
  284. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.js +0 -62
  285. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.js.map +0 -1
  286. package/src/I18nWrapper.tsx +0 -13
  287. package/src/__snapshots__/index.test.tsx.snap +0 -96
  288. package/src/components/AgeVerificationModal/AgeVerificationModal.css +0 -95
  289. package/src/components/AgeVerificationModal/AgeVerificationModal.stories.tsx +0 -46
  290. package/src/components/AgeVerificationModal/AgeVerificationModal.test.tsx +0 -33
  291. package/src/components/AgeVerificationModal/AgeVerificationModal.tsx +0 -99
  292. package/src/components/AgeVerificationModal/__snapshots__/AgeVerificationModal.test.tsx.snap +0 -19
  293. package/src/components/Auth/Auth.css +0 -57
  294. package/src/components/Auth/Auth.stories.tsx +0 -90
  295. package/src/components/Auth/Auth.test.tsx +0 -63
  296. package/src/components/Auth/Auth.tsx +0 -201
  297. package/src/components/Auth/__snapshots__/Auth.test.tsx.snap +0 -143
  298. package/src/components/Avatar/Avatar.css +0 -151
  299. package/src/components/Avatar/Avatar.stories.tsx +0 -160
  300. package/src/components/Avatar/Avatar.test.tsx +0 -158
  301. package/src/components/Avatar/Avatar.tsx +0 -245
  302. package/src/components/Avatar/AvatarView/AvatarComponent/Shadow/DynamicShadow.tsx +0 -103
  303. package/src/components/Avatar/AvatarView/AvatarComponent/avatarComponent.tsx +0 -105
  304. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.tsx +0 -338
  305. package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/types.ts +0 -44
  306. package/src/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarAnimator.ts +0 -1091
  307. package/src/components/Avatar/AvatarView/AvatarComponent/components/controllers/AvatarPositionController.ts +0 -83
  308. package/src/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.ts +0 -301
  309. package/src/components/Avatar/AvatarView/AvatarComponent/components/controls.tsx +0 -100
  310. package/src/components/Avatar/AvatarView/AvatarComponent/components/halfbodyAvatar.tsx +0 -152
  311. package/src/components/Avatar/AvatarView/AvatarComponent/components/loader.tsx +0 -31
  312. package/src/components/Avatar/AvatarView/AvatarComponent/constants.ts +0 -141
  313. package/src/components/Avatar/AvatarView/AvatarComponent/lights/Lights.tsx +0 -145
  314. package/src/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.css +0 -111
  315. package/src/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.tsx +0 -201
  316. package/src/components/Avatar/AvatarView/AvatarView.stories.tsx +0 -254
  317. package/src/components/Avatar/AvatarView/index.tsx +0 -194
  318. package/src/components/Avatar/AvatarView/utils/hideHands.ts +0 -11
  319. package/src/components/Avatar/AvatarView/utils/useEyeBlink.ts +0 -100
  320. package/src/components/Avatar/AvatarView/utils/useHeadMovement.ts +0 -72
  321. package/src/components/Avatar/AvatarView/utils/useLoadingMorphAnim.ts +0 -38
  322. package/src/components/Avatar/AvatarView/utils/useMouthAnimation.ts +0 -93
  323. package/src/components/Avatar/AvatarView/utils/useSmile.ts +0 -39
  324. package/src/components/Avatar/AvatarView/utils/utils.ts +0 -55
  325. package/src/components/Avatar/__snapshots__/Avatar.test.tsx.snap +0 -246
  326. package/src/components/Blob/Blob.css +0 -286
  327. package/src/components/Blob/Blob.stories.tsx +0 -53
  328. package/src/components/Blob/Blob.test.tsx +0 -18
  329. package/src/components/Blob/Blob.tsx +0 -26
  330. package/src/components/Blob/__snapshots__/Blob.test.tsx.snap +0 -80
  331. package/src/components/BlockedMemoriBadge/BlockedMemoriBadge.css +0 -40
  332. package/src/components/BlockedMemoriBadge/BlockedMemoriBadge.stories.tsx +0 -58
  333. package/src/components/BlockedMemoriBadge/BlockedMemoriBadge.test.tsx +0 -53
  334. package/src/components/BlockedMemoriBadge/BlockedMemoriBadge.tsx +0 -74
  335. package/src/components/BlockedMemoriBadge/__snapshots__/BlockedMemoriBadge.test.tsx.snap +0 -181
  336. package/src/components/ChangeMode/ChangeMode.css +0 -22
  337. package/src/components/ChangeMode/ChangeMode.stories.tsx +0 -50
  338. package/src/components/ChangeMode/ChangeMode.test.tsx +0 -24
  339. package/src/components/ChangeMode/ChangeMode.tsx +0 -51
  340. package/src/components/ChangeMode/__snapshots__/ChangeMode.test.tsx.snap +0 -107
  341. package/src/components/Chat/Chat.css +0 -285
  342. package/src/components/Chat/Chat.stories.tsx +0 -1684
  343. package/src/components/Chat/Chat.test.tsx +0 -372
  344. package/src/components/Chat/Chat.tsx +0 -778
  345. package/src/components/Chat/__snapshots__/Chat.test.tsx.snap +0 -6816
  346. package/src/components/ChatBubble/ChatBubble.css +0 -430
  347. package/src/components/ChatBubble/ChatBubble.stories.tsx +0 -1192
  348. package/src/components/ChatBubble/ChatBubble.test.tsx +0 -481
  349. package/src/components/ChatBubble/ChatBubble.tsx +0 -776
  350. package/src/components/ChatBubble/__snapshots__/ChatBubble.test.tsx.snap +0 -8734
  351. package/src/components/ChatHistoryDrawer/ChatHistory.css +0 -587
  352. package/src/components/ChatHistoryDrawer/ChatHistory.stories.tsx +0 -410
  353. package/src/components/ChatHistoryDrawer/ChatHistory.test.tsx +0 -107
  354. package/src/components/ChatHistoryDrawer/ChatHistory.tsx +0 -1298
  355. package/src/components/ChatHistoryDrawer/__snapshots__/ChatHistory.test.tsx.snap +0 -27
  356. package/src/components/ChatInputs/ChatInputs.css +0 -340
  357. package/src/components/ChatInputs/ChatInputs.stories.tsx +0 -251
  358. package/src/components/ChatInputs/ChatInputs.tsx +0 -487
  359. package/src/components/ChatTextArea/ChatTextArea.css +0 -185
  360. package/src/components/ChatTextArea/ChatTextArea.stories.tsx +0 -55
  361. package/src/components/ChatTextArea/ChatTextArea.test.tsx +0 -28
  362. package/src/components/ChatTextArea/ChatTextArea.tsx +0 -171
  363. package/src/components/ChatTextArea/__snapshots__/ChatTextArea.test.tsx.snap +0 -68
  364. package/src/components/CompletionProviderStatus/CompletionProviderStatus.css +0 -36
  365. package/src/components/CompletionProviderStatus/CompletionProviderStatus.stories.tsx +0 -296
  366. package/src/components/CompletionProviderStatus/CompletionProviderStatus.test.tsx +0 -31
  367. package/src/components/CompletionProviderStatus/CompletionProviderStatus.tsx +0 -232
  368. package/src/components/CompletionProviderStatus/__snapshots__/CompletionProviderStatus.test.tsx.snap +0 -96
  369. package/src/components/ContentPreviewModal/ContentPreviewModal.css +0 -128
  370. package/src/components/ContentPreviewModal/ContentPreviewModal.tsx +0 -67
  371. package/src/components/ContentPreviewModal/index.ts +0 -2
  372. package/src/components/CustomGLBModelViewer/ModelViewer.css +0 -11
  373. package/src/components/CustomGLBModelViewer/ModelViewer.stories.tsx +0 -58
  374. package/src/components/CustomGLBModelViewer/ModelViewer.test.tsx +0 -13
  375. package/src/components/CustomGLBModelViewer/ModelViewer.tsx +0 -37
  376. package/src/components/CustomGLBModelViewer/__snapshots__/ModelViewer.test.tsx.snap +0 -20
  377. package/src/components/DateSelector/DateSelector.css +0 -158
  378. package/src/components/DateSelector/DateSelector.stories.tsx +0 -47
  379. package/src/components/DateSelector/DateSelector.test.tsx +0 -149
  380. package/src/components/DateSelector/DateSelector.tsx +0 -251
  381. package/src/components/DateSelector/__snapshots__/DateSelector.test.tsx.snap +0 -1954
  382. package/src/components/ErrorBoundary/ErrorBoundary.tsx +0 -54
  383. package/src/components/ExpertsDrawer/ExpertsDrawer.css +0 -67
  384. package/src/components/ExpertsDrawer/ExpertsDrawer.stories.tsx +0 -54
  385. package/src/components/ExpertsDrawer/ExpertsDrawer.test.tsx +0 -53
  386. package/src/components/ExpertsDrawer/ExpertsDrawer.tsx +0 -69
  387. package/src/components/ExpertsDrawer/__snapshots__/ExpertsDrawer.test.tsx.snap +0 -11
  388. package/src/components/ExportHistoryButton/ExportHistoryButton.css +0 -22
  389. package/src/components/FeedbackButtons/FeedbackButtons.css +0 -107
  390. package/src/components/FeedbackButtons/FeedbackButtons.stories.tsx +0 -52
  391. package/src/components/FeedbackButtons/FeedbackButtons.test.tsx +0 -11
  392. package/src/components/FeedbackButtons/FeedbackButtons.tsx +0 -152
  393. package/src/components/FeedbackButtons/__snapshots__/FeedbackButtons.test.tsx.snap +0 -42
  394. package/src/components/FilePreview/FilePreview.css +0 -358
  395. package/src/components/FilePreview/FilePreview.stories.tsx +0 -63
  396. package/src/components/FilePreview/FilePreview.test.tsx +0 -26
  397. package/src/components/FilePreview/FilePreview.tsx +0 -277
  398. package/src/components/FilePreview/__snapshots__/FilePreview.test.tsx.snap +0 -185
  399. package/src/components/Header/ChatConsumptionDropdown.test.tsx +0 -117
  400. package/src/components/Header/ChatConsumptionDropdown.tsx +0 -275
  401. package/src/components/Header/Header.css +0 -282
  402. package/src/components/Header/Header.stories.tsx +0 -338
  403. package/src/components/Header/Header.test.tsx +0 -371
  404. package/src/components/Header/Header.tsx +0 -442
  405. package/src/components/Header/__snapshots__/Header.test.tsx.snap +0 -964
  406. package/src/components/KnownFacts/KnownFacts.css +0 -19
  407. package/src/components/KnownFacts/KnownFacts.stories.tsx +0 -80
  408. package/src/components/KnownFacts/KnownFacts.test.tsx +0 -57
  409. package/src/components/KnownFacts/KnownFacts.tsx +0 -376
  410. package/src/components/KnownFacts/__snapshots__/KnownFacts.test.tsx.snap +0 -19
  411. package/src/components/LoginDrawer/LoginDrawer.css +0 -1616
  412. package/src/components/LoginDrawer/LoginDrawer.stories.tsx +0 -102
  413. package/src/components/LoginDrawer/LoginDrawer.test.tsx +0 -199
  414. package/src/components/LoginDrawer/LoginDrawer.tsx +0 -600
  415. package/src/components/LoginDrawer/__snapshots__/LoginDrawer.test.tsx.snap +0 -91
  416. package/src/components/MediaWidget/DocumentCard.test.tsx +0 -45
  417. package/src/components/MediaWidget/DocumentCard.tsx +0 -19
  418. package/src/components/MediaWidget/MediaItemWidget.css +0 -1398
  419. package/src/components/MediaWidget/MediaItemWidget.stories.tsx +0 -423
  420. package/src/components/MediaWidget/MediaItemWidget.test.tsx +0 -248
  421. package/src/components/MediaWidget/MediaItemWidget.tsx +0 -997
  422. package/src/components/MediaWidget/MediaItemWidget.types.ts +0 -65
  423. package/src/components/MediaWidget/MediaItemWidget.utils.test.ts +0 -367
  424. package/src/components/MediaWidget/MediaItemWidget.utils.ts +0 -227
  425. package/src/components/MediaWidget/MediaPreviewModal.test.tsx +0 -271
  426. package/src/components/MediaWidget/MediaPreviewModal.tsx +0 -425
  427. package/src/components/MediaWidget/MediaWidget.css +0 -131
  428. package/src/components/MediaWidget/MediaWidget.stories.tsx +0 -319
  429. package/src/components/MediaWidget/MediaWidget.test.tsx +0 -41
  430. package/src/components/MediaWidget/MediaWidget.tsx +0 -107
  431. package/src/components/MediaWidget/__snapshots__/DocumentCard.test.tsx.snap +0 -24
  432. package/src/components/MediaWidget/__snapshots__/MediaItemWidget.test.tsx.snap +0 -791
  433. package/src/components/MediaWidget/__snapshots__/MediaWidget.test.tsx.snap +0 -154
  434. package/src/components/MemoriArtifactSystem/ArtifactDrawer.stories.tsx +0 -2299
  435. package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +0 -163
  436. package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.tsx +0 -250
  437. package/src/components/MemoriArtifactSystem/components/ArtifactActions/CopyButton.stories.tsx +0 -30
  438. package/src/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.tsx +0 -467
  439. package/src/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.tsx +0 -89
  440. package/src/components/MemoriArtifactSystem/components/ArtifactActions/hooks/useCopyArtifact.ts +0 -619
  441. package/src/components/MemoriArtifactSystem/components/ArtifactActions/index.ts +0 -9
  442. package/src/components/MemoriArtifactSystem/components/ArtifactActions/styles.css +0 -489
  443. package/src/components/MemoriArtifactSystem/components/ArtifactActions/test/CopyButtonTest.tsx +0 -90
  444. package/src/components/MemoriArtifactSystem/components/ArtifactActions/types.ts +0 -73
  445. package/src/components/MemoriArtifactSystem/components/ArtifactActions/utils/PDFExporter.ts +0 -454
  446. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +0 -1059
  447. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.tsx +0 -431
  448. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.css +0 -150
  449. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/components/TabSwitch.tsx +0 -79
  450. package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +0 -253
  451. package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.tsx +0 -561
  452. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +0 -355
  453. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.tsx +0 -160
  454. package/src/components/MemoriArtifactSystem/context/ArtifactContext.tsx +0 -167
  455. package/src/components/MemoriArtifactSystem/types/artifact.types.ts +0 -35
  456. package/src/components/MemoriArtifactSystem/utils/ArtifactAPI.test.tsx +0 -308
  457. package/src/components/MemoriArtifactSystem/utils/ArtifactAPI.tsx +0 -379
  458. package/src/components/MemoriArtifactSystem/utils/applyEdits.test.ts +0 -85
  459. package/src/components/MemoriArtifactSystem/utils/applyEdits.ts +0 -173
  460. package/src/components/MemoriWidget/MemoriWidget.css +0 -220
  461. package/src/components/MemoriWidget/MemoriWidget.stories.tsx +0 -274
  462. package/src/components/MemoriWidget/MemoriWidget.tsx +0 -3930
  463. package/src/components/MicrophoneButton/MicrophoneButton.css +0 -142
  464. package/src/components/MicrophoneButton/MicrophoneButton.stories.tsx +0 -52
  465. package/src/components/MicrophoneButton/MicrophoneButton.tsx +0 -128
  466. package/src/components/PositionDrawer/PositionDrawer.stories.tsx +0 -51
  467. package/src/components/PositionDrawer/PositionDrawer.test.tsx +0 -39
  468. package/src/components/PositionDrawer/PositionDrawer.tsx +0 -50
  469. package/src/components/PositionDrawer/__snapshots__/PositionDrawer.test.tsx.snap +0 -17
  470. package/src/components/PoweredBy/PoweredBy.css +0 -47
  471. package/src/components/PoweredBy/PoweredBy.stories.tsx +0 -75
  472. package/src/components/PoweredBy/PoweredBy.test.tsx +0 -15
  473. package/src/components/PoweredBy/PoweredBy.tsx +0 -44
  474. package/src/components/PoweredBy/__snapshots__/PoweredBy.test.tsx.snap +0 -55
  475. package/src/components/SendOnEnterMenu/SendOnEnterMenu.css +0 -47
  476. package/src/components/SendOnEnterMenu/SendOnEnterMenu.stories.tsx +0 -58
  477. package/src/components/SendOnEnterMenu/SendOnEnterMenu.test.tsx +0 -17
  478. package/src/components/SendOnEnterMenu/SendOnEnterMenu.tsx +0 -82
  479. package/src/components/SendOnEnterMenu/__snapshots__/SendOnEnterMenu.test.tsx.snap +0 -57
  480. package/src/components/SettingsDrawer/SettingsDrawer.css +0 -24
  481. package/src/components/SettingsDrawer/SettingsDrawer.stories.tsx +0 -94
  482. package/src/components/SettingsDrawer/SettingsDrawer.test.tsx +0 -202
  483. package/src/components/SettingsDrawer/SettingsDrawer.tsx +0 -255
  484. package/src/components/SettingsDrawer/__snapshots__/SettingsDrawer.test.tsx.snap +0 -59
  485. package/src/components/ShareButton/ShareButton.css +0 -73
  486. package/src/components/ShareButton/ShareButton.stories.tsx +0 -98
  487. package/src/components/ShareButton/ShareButton.test.tsx +0 -53
  488. package/src/components/ShareButton/ShareButton.tsx +0 -395
  489. package/src/components/ShareButton/__snapshots__/ShareButton.test.tsx.snap +0 -316
  490. package/src/components/Snippet/Snippet.css +0 -142
  491. package/src/components/Snippet/Snippet.stories.tsx +0 -286
  492. package/src/components/Snippet/Snippet.test.tsx +0 -176
  493. package/src/components/Snippet/Snippet.tsx +0 -148
  494. package/src/components/Snippet/__snapshots__/Snippet.test.tsx.snap +0 -603
  495. package/src/components/StartPanel/StartPanel.css +0 -273
  496. package/src/components/StartPanel/StartPanel.stories.tsx +0 -404
  497. package/src/components/StartPanel/StartPanel.test.tsx +0 -360
  498. package/src/components/StartPanel/StartPanel.tsx +0 -426
  499. package/src/components/StartPanel/__snapshots__/StartPanel.test.tsx.snap +0 -1986
  500. package/src/components/Typing/Typing.stories.tsx +0 -130
  501. package/src/components/Typing/Typing.test.tsx +0 -62
  502. package/src/components/Typing/Typing.tsx +0 -154
  503. package/src/components/Typing/__snapshots__/Typing.test.tsx.snap +0 -133
  504. package/src/components/UploadButton/UploadButton.css +0 -542
  505. package/src/components/UploadButton/UploadButton.stories.tsx +0 -376
  506. package/src/components/UploadButton/UploadButton.test.tsx +0 -11
  507. package/src/components/UploadButton/UploadButton.tsx +0 -688
  508. package/src/components/UploadButton/UploadDocuments/UploadDocuments.tsx +0 -421
  509. package/src/components/UploadButton/UploadImages/UploadImages.tsx +0 -538
  510. package/src/components/UploadButton/__snapshots__/UploadButton.test.tsx.snap +0 -165
  511. package/src/components/VenueWidget/VenueWidget.css +0 -163
  512. package/src/components/VenueWidget/VenueWidget.stories.tsx +0 -66
  513. package/src/components/VenueWidget/VenueWidget.test.tsx +0 -49
  514. package/src/components/VenueWidget/VenueWidget.tsx +0 -458
  515. package/src/components/VenueWidget/__snapshots__/VenueWidget.test.tsx.snap +0 -370
  516. package/src/components/WhyThisAnswer/WhyThisAnswer.css +0 -171
  517. package/src/components/WhyThisAnswer/WhyThisAnswer.stories.tsx +0 -285
  518. package/src/components/WhyThisAnswer/WhyThisAnswer.test.tsx +0 -245
  519. package/src/components/WhyThisAnswer/WhyThisAnswer.tsx +0 -233
  520. package/src/components/WhyThisAnswer/__snapshots__/WhyThisAnswer.test.tsx.snap +0 -27
  521. package/src/components/icons/AI.tsx +0 -34
  522. package/src/components/icons/Alert.tsx +0 -31
  523. package/src/components/icons/ArrowUp.tsx +0 -28
  524. package/src/components/icons/Bug.tsx +0 -81
  525. package/src/components/icons/Chat.tsx +0 -30
  526. package/src/components/icons/ChevronDown.tsx +0 -26
  527. package/src/components/icons/ChevronLeft.tsx +0 -28
  528. package/src/components/icons/ChevronRight.tsx +0 -28
  529. package/src/components/icons/ChevronUp.tsx +0 -24
  530. package/src/components/icons/Clear.tsx +0 -23
  531. package/src/components/icons/Close.tsx +0 -30
  532. package/src/components/icons/Code.tsx +0 -24
  533. package/src/components/icons/Copy.tsx +0 -23
  534. package/src/components/icons/DeepThought.tsx +0 -38
  535. package/src/components/icons/Delete.tsx +0 -23
  536. package/src/components/icons/Document.tsx +0 -50
  537. package/src/components/icons/Download.tsx +0 -23
  538. package/src/components/icons/Edit.tsx +0 -17
  539. package/src/components/icons/Expand.tsx +0 -23
  540. package/src/components/icons/Eye.tsx +0 -17
  541. package/src/components/icons/EyeInvisible.tsx +0 -24
  542. package/src/components/icons/Facebook.tsx +0 -23
  543. package/src/components/icons/Feedback.tsx +0 -24
  544. package/src/components/icons/File.tsx +0 -17
  545. package/src/components/icons/FileExcel.tsx +0 -23
  546. package/src/components/icons/FilePdf.tsx +0 -23
  547. package/src/components/icons/FileWord.tsx +0 -23
  548. package/src/components/icons/Fullscreen.tsx +0 -23
  549. package/src/components/icons/FullscreenExit.tsx +0 -23
  550. package/src/components/icons/GasStation.tsx +0 -36
  551. package/src/components/icons/Group.tsx +0 -30
  552. package/src/components/icons/History.tsx +0 -33
  553. package/src/components/icons/Image.tsx +0 -37
  554. package/src/components/icons/Info.tsx +0 -31
  555. package/src/components/icons/Link.tsx +0 -17
  556. package/src/components/icons/Linkedin.tsx +0 -23
  557. package/src/components/icons/Loading.tsx +0 -28
  558. package/src/components/icons/Logout.tsx +0 -27
  559. package/src/components/icons/Mail.tsx +0 -17
  560. package/src/components/icons/MapMarker.tsx +0 -26
  561. package/src/components/icons/MenuHorizontal.tsx +0 -29
  562. package/src/components/icons/MenuVertical.tsx +0 -29
  563. package/src/components/icons/Message.tsx +0 -23
  564. package/src/components/icons/Microphone.tsx +0 -23
  565. package/src/components/icons/Minus.tsx +0 -23
  566. package/src/components/icons/MinusCircle.tsx +0 -24
  567. package/src/components/icons/PaperClip.tsx +0 -23
  568. package/src/components/icons/Picture.tsx +0 -23
  569. package/src/components/icons/Plus.tsx +0 -18
  570. package/src/components/icons/Preview.tsx +0 -29
  571. package/src/components/icons/Print.tsx +0 -38
  572. package/src/components/icons/QuestionHelp.tsx +0 -30
  573. package/src/components/icons/Refresh.tsx +0 -30
  574. package/src/components/icons/SelectIcon.tsx +0 -28
  575. package/src/components/icons/Send.tsx +0 -17
  576. package/src/components/icons/Setting.tsx +0 -23
  577. package/src/components/icons/Share.tsx +0 -23
  578. package/src/components/icons/Sound.tsx +0 -23
  579. package/src/components/icons/SoundDeactivated.tsx +0 -27
  580. package/src/components/icons/Telegram.tsx +0 -26
  581. package/src/components/icons/ThumbDown.tsx +0 -29
  582. package/src/components/icons/ThumbUp.tsx +0 -29
  583. package/src/components/icons/Translation.tsx +0 -24
  584. package/src/components/icons/Twitter.tsx +0 -23
  585. package/src/components/icons/Upload.tsx +0 -34
  586. package/src/components/icons/User.tsx +0 -17
  587. package/src/components/icons/Warning.tsx +0 -24
  588. package/src/components/icons/WhatsApp.tsx +0 -24
  589. package/src/components/icons/icons.stories.css +0 -5
  590. package/src/components/icons/icons.stories.tsx +0 -81
  591. package/src/components/icons/loading.css +0 -14
  592. package/src/components/layouts/Chat.test.tsx +0 -40
  593. package/src/components/layouts/Chat.tsx +0 -69
  594. package/src/components/layouts/FullBody/FullBody.stories.tsx +0 -110
  595. package/src/components/layouts/FullPage.test.tsx +0 -65
  596. package/src/components/layouts/FullPage.tsx +0 -80
  597. package/src/components/layouts/HiddenChat.test.tsx +0 -40
  598. package/src/components/layouts/HiddenChat.tsx +0 -233
  599. package/src/components/layouts/Totem/Totem.stories.tsx +0 -131
  600. package/src/components/layouts/Totem.test.tsx +0 -40
  601. package/src/components/layouts/Totem.tsx +0 -49
  602. package/src/components/layouts/WebsiteAssistant.test.tsx +0 -40
  603. package/src/components/layouts/WebsiteAssistant.tsx +0 -132
  604. package/src/components/layouts/ZoomedFullBody/ZoomedFullBody.stories.tsx +0 -131
  605. package/src/components/layouts/ZoomedFullBody.test.tsx +0 -40
  606. package/src/components/layouts/ZoomedFullBody.tsx +0 -81
  607. package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +0 -457
  608. package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +0 -1045
  609. package/src/components/layouts/__snapshots__/HiddenChat.test.tsx.snap +0 -460
  610. package/src/components/layouts/__snapshots__/Totem.test.tsx.snap +0 -566
  611. package/src/components/layouts/__snapshots__/WebsiteAssistant.test.tsx.snap +0 -59
  612. package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +0 -523
  613. package/src/components/layouts/chat.css +0 -451
  614. package/src/components/layouts/hidden-chat.css +0 -276
  615. package/src/components/layouts/layouts.stories.tsx +0 -231
  616. package/src/components/layouts/totem.css +0 -221
  617. package/src/components/layouts/zoomed-full-body.css +0 -18
  618. package/src/components/ui/Alert.css +0 -93
  619. package/src/components/ui/Alert.stories.tsx +0 -137
  620. package/src/components/ui/Alert.test.tsx +0 -129
  621. package/src/components/ui/Alert.tsx +0 -123
  622. package/src/components/ui/Button.css +0 -167
  623. package/src/components/ui/Button.stories.tsx +0 -156
  624. package/src/components/ui/Button.test.tsx +0 -74
  625. package/src/components/ui/Button.tsx +0 -107
  626. package/src/components/ui/Card.css +0 -65
  627. package/src/components/ui/Card.stories.tsx +0 -95
  628. package/src/components/ui/Card.test.tsx +0 -47
  629. package/src/components/ui/Card.tsx +0 -48
  630. package/src/components/ui/Checkbox.css +0 -116
  631. package/src/components/ui/Checkbox.stories.tsx +0 -74
  632. package/src/components/ui/Checkbox.test.tsx +0 -28
  633. package/src/components/ui/Checkbox.tsx +0 -58
  634. package/src/components/ui/ConfirmDialog.css +0 -42
  635. package/src/components/ui/ConfirmDialog.stories.tsx +0 -216
  636. package/src/components/ui/ConfirmDialog.test.tsx +0 -124
  637. package/src/components/ui/ConfirmDialog.tsx +0 -58
  638. package/src/components/ui/Details.css +0 -66
  639. package/src/components/ui/Drawer.css +0 -270
  640. package/src/components/ui/Drawer.stories.tsx +0 -267
  641. package/src/components/ui/Drawer.test.tsx +0 -130
  642. package/src/components/ui/Drawer.tsx +0 -293
  643. package/src/components/ui/Dropdown.css +0 -173
  644. package/src/components/ui/Dropdown.tsx +0 -63
  645. package/src/components/ui/Expandable.css +0 -15
  646. package/src/components/ui/Expandable.stories.tsx +0 -180
  647. package/src/components/ui/Expandable.test.tsx +0 -107
  648. package/src/components/ui/Expandable.tsx +0 -121
  649. package/src/components/ui/Modal.css +0 -98
  650. package/src/components/ui/Modal.stories.tsx +0 -171
  651. package/src/components/ui/Modal.test.tsx +0 -111
  652. package/src/components/ui/Modal.tsx +0 -117
  653. package/src/components/ui/Select.css +0 -138
  654. package/src/components/ui/Select.stories.tsx +0 -79
  655. package/src/components/ui/Select.test.tsx +0 -84
  656. package/src/components/ui/Select.tsx +0 -79
  657. package/src/components/ui/Slider.css +0 -192
  658. package/src/components/ui/Slider.stories.tsx +0 -63
  659. package/src/components/ui/Slider.tsx +0 -142
  660. package/src/components/ui/Spin.css +0 -31
  661. package/src/components/ui/Spin.stories.tsx +0 -64
  662. package/src/components/ui/Spin.test.tsx +0 -41
  663. package/src/components/ui/Spin.tsx +0 -31
  664. package/src/components/ui/Table.css +0 -132
  665. package/src/components/ui/Tooltip.css +0 -171
  666. package/src/components/ui/Tooltip.stories.tsx +0 -109
  667. package/src/components/ui/Tooltip.test.tsx +0 -102
  668. package/src/components/ui/Tooltip.tsx +0 -163
  669. package/src/components/ui/__snapshots__/Alert.test.tsx.snap +0 -59
  670. package/src/components/ui/__snapshots__/Button.test.tsx.snap +0 -204
  671. package/src/components/ui/__snapshots__/Card.test.tsx.snap +0 -321
  672. package/src/components/ui/__snapshots__/Checkbox.test.tsx.snap +0 -131
  673. package/src/components/ui/__snapshots__/ConfirmDialog.test.tsx.snap +0 -35
  674. package/src/components/ui/__snapshots__/Drawer.test.tsx.snap +0 -67
  675. package/src/components/ui/__snapshots__/Expandable.test.tsx.snap +0 -159
  676. package/src/components/ui/__snapshots__/Modal.test.tsx.snap +0 -59
  677. package/src/components/ui/__snapshots__/Select.test.tsx.snap +0 -254
  678. package/src/components/ui/__snapshots__/Spin.test.tsx.snap +0 -193
  679. package/src/components/ui/__snapshots__/Tooltip.test.tsx.snap +0 -113
  680. package/src/components/ui/definitions.stories.tsx +0 -139
  681. package/src/context/visemeContext.tsx +0 -384
  682. package/src/helpers/chatPdfExport.ts +0 -544
  683. package/src/helpers/configuration.ts +0 -42
  684. package/src/helpers/constants.ts +0 -268
  685. package/src/helpers/convertDocument.test.ts +0 -67
  686. package/src/helpers/convertDocument.ts +0 -35
  687. package/src/helpers/credits.ts +0 -54
  688. package/src/helpers/error.test.ts +0 -29
  689. package/src/helpers/error.ts +0 -278
  690. package/src/helpers/imageCompression.ts +0 -230
  691. package/src/helpers/llmUsage.ts +0 -328
  692. package/src/helpers/markedExtendedTables.js +0 -217
  693. package/src/helpers/media.test.ts +0 -169
  694. package/src/helpers/media.ts +0 -81
  695. package/src/helpers/message.test.ts +0 -126
  696. package/src/helpers/message.ts +0 -219
  697. package/src/helpers/nats/getNatsConfig.ts +0 -148
  698. package/src/helpers/nats/isSessionExpiredError.test.ts +0 -38
  699. package/src/helpers/nats/isSessionExpiredError.ts +0 -51
  700. package/src/helpers/nats/useNats.test.tsx +0 -81
  701. package/src/helpers/nats/useNats.ts +0 -204
  702. package/src/helpers/nats/useNatsSession.test.ts +0 -16
  703. package/src/helpers/nats/useNatsSession.ts +0 -460
  704. package/src/helpers/piiDetection.test.ts +0 -186
  705. package/src/helpers/piiDetection.ts +0 -91
  706. package/src/helpers/sanitizer.ts +0 -17
  707. package/src/helpers/statistics.ts +0 -58
  708. package/src/helpers/stt/useSTT.ts +0 -515
  709. package/src/helpers/translations.ts +0 -39
  710. package/src/helpers/tts/ttsVoiceUtility.ts +0 -275
  711. package/src/helpers/tts/useTTS.ts +0 -621
  712. package/src/helpers/utils.test.ts +0 -331
  713. package/src/helpers/utils.ts +0 -591
  714. package/src/helpers/venue.ts +0 -36
  715. package/src/i18n.ts +0 -39
  716. package/src/index.stories.tsx +0 -220
  717. package/src/index.test.tsx +0 -156
  718. package/src/index.tsx +0 -592
  719. package/src/locales/de.json +0 -680
  720. package/src/locales/en.json +0 -754
  721. package/src/locales/es.json +0 -682
  722. package/src/locales/fr.json +0 -682
  723. package/src/locales/it.json +0 -750
  724. package/src/mocks/data.ts +0 -790
  725. package/src/styles.css +0 -126
  726. package/src/types/integration.test.ts +0 -32
  727. package/src/types/integration.ts +0 -31
  728. package/src/types/layout.ts +0 -50
  729. 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;