@memori.ai/memori-react 8.11.0 → 8.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/components/AgeVerificationModal/AgeVerificationModal.css +41 -14
  3. package/dist/components/AgeVerificationModal/AgeVerificationModal.js +2 -2
  4. package/dist/components/AgeVerificationModal/AgeVerificationModal.js.map +1 -1
  5. package/dist/components/Auth/Auth.js +36 -8
  6. package/dist/components/Auth/Auth.js.map +1 -1
  7. package/dist/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.css +2 -2
  8. package/dist/components/Chat/Chat.css +37 -3
  9. package/dist/components/Chat/Chat.js +61 -23
  10. package/dist/components/Chat/Chat.js.map +1 -1
  11. package/dist/components/ChatBubble/ChatBubble.css +87 -15
  12. package/dist/components/ChatBubble/ChatBubble.js +129 -19
  13. package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
  14. package/dist/components/ChatHistoryDrawer/ChatHistory.css +5 -1
  15. package/dist/components/ChatInputs/ChatInputs.css +293 -17
  16. package/dist/components/ChatInputs/ChatInputs.d.ts +1 -0
  17. package/dist/components/ChatInputs/ChatInputs.js +48 -27
  18. package/dist/components/ChatInputs/ChatInputs.js.map +1 -1
  19. package/dist/components/ChatTextArea/ChatTextArea.css +75 -31
  20. package/dist/components/ChatTextArea/ChatTextArea.js +47 -18
  21. package/dist/components/ChatTextArea/ChatTextArea.js.map +1 -1
  22. package/dist/components/DateSelector/DateSelector.css +125 -104
  23. package/dist/components/DateSelector/DateSelector.d.ts +1 -1
  24. package/dist/components/DateSelector/DateSelector.js +110 -52
  25. package/dist/components/DateSelector/DateSelector.js.map +1 -1
  26. package/dist/components/FilePreview/FilePreview.css +225 -146
  27. package/dist/components/FilePreview/FilePreview.d.ts +1 -2
  28. package/dist/components/FilePreview/FilePreview.js +20 -6
  29. package/dist/components/FilePreview/FilePreview.js.map +1 -1
  30. package/dist/components/Header/Header.css +2 -2
  31. package/dist/components/Header/Header.js +1 -1
  32. package/dist/components/Header/Header.js.map +1 -1
  33. package/dist/components/LoginDrawer/LoginDrawer.css +37 -5
  34. package/dist/components/LoginDrawer/LoginDrawer.d.ts +1 -2
  35. package/dist/components/LoginDrawer/LoginDrawer.js +2 -9
  36. package/dist/components/LoginDrawer/LoginDrawer.js.map +1 -1
  37. package/dist/components/MediaWidget/MediaItemWidget.js +2 -1
  38. package/dist/components/MediaWidget/MediaItemWidget.js.map +1 -1
  39. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js +1 -1
  40. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js.map +1 -1
  41. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.js +1 -1
  42. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.js.map +1 -1
  43. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.js +3 -0
  44. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.js.map +1 -1
  45. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js +2 -2
  46. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js.map +1 -1
  47. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +16 -7
  48. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +6 -4
  49. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -1
  50. package/dist/components/MemoriWidget/MemoriWidget.css +11 -2
  51. package/dist/components/MemoriWidget/MemoriWidget.js +105 -25
  52. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  53. package/dist/components/MicrophoneButton/MicrophoneButton.css +2 -2
  54. package/dist/components/StartPanel/StartPanel.css +8 -0
  55. package/dist/components/UploadButton/UploadButton.css +20 -17
  56. package/dist/components/UploadButton/UploadButton.js +218 -87
  57. package/dist/components/UploadButton/UploadButton.js.map +1 -1
  58. package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js +14 -4
  59. package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
  60. package/dist/components/UploadButton/UploadImages/UploadImages.js +143 -16
  61. package/dist/components/UploadButton/UploadImages/UploadImages.js.map +1 -1
  62. package/dist/components/layouts/chat.css +1 -1
  63. package/dist/components/ui/Drawer.css +8 -0
  64. package/dist/components/ui/Drawer.d.ts +2 -0
  65. package/dist/components/ui/Drawer.js +2 -2
  66. package/dist/components/ui/Drawer.js.map +1 -1
  67. package/dist/components/ui/Tooltip.css +49 -1
  68. package/dist/components/ui/Tooltip.d.ts +1 -1
  69. package/dist/helpers/constants.d.ts +1 -0
  70. package/dist/helpers/constants.js +2 -1
  71. package/dist/helpers/constants.js.map +1 -1
  72. package/dist/helpers/imageCompression.d.ts +7 -0
  73. package/dist/helpers/imageCompression.js +123 -0
  74. package/dist/helpers/imageCompression.js.map +1 -0
  75. package/dist/locales/de.json +13 -5
  76. package/dist/locales/en.json +17 -6
  77. package/dist/locales/es.json +13 -5
  78. package/dist/locales/fr.json +12 -5
  79. package/dist/locales/it.json +16 -6
  80. package/dist/styles.css +4 -4
  81. package/esm/components/AgeVerificationModal/AgeVerificationModal.css +41 -14
  82. package/esm/components/AgeVerificationModal/AgeVerificationModal.js +2 -2
  83. package/esm/components/AgeVerificationModal/AgeVerificationModal.js.map +1 -1
  84. package/esm/components/Auth/Auth.js +36 -8
  85. package/esm/components/Auth/Auth.js.map +1 -1
  86. package/esm/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.css +2 -2
  87. package/esm/components/Chat/Chat.css +37 -3
  88. package/esm/components/Chat/Chat.js +61 -23
  89. package/esm/components/Chat/Chat.js.map +1 -1
  90. package/esm/components/ChatBubble/ChatBubble.css +87 -15
  91. package/esm/components/ChatBubble/ChatBubble.js +130 -20
  92. package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
  93. package/esm/components/ChatHistoryDrawer/ChatHistory.css +5 -1
  94. package/esm/components/ChatInputs/ChatInputs.css +293 -17
  95. package/esm/components/ChatInputs/ChatInputs.d.ts +1 -0
  96. package/esm/components/ChatInputs/ChatInputs.js +49 -28
  97. package/esm/components/ChatInputs/ChatInputs.js.map +1 -1
  98. package/esm/components/ChatTextArea/ChatTextArea.css +75 -31
  99. package/esm/components/ChatTextArea/ChatTextArea.js +49 -20
  100. package/esm/components/ChatTextArea/ChatTextArea.js.map +1 -1
  101. package/esm/components/DateSelector/DateSelector.css +125 -104
  102. package/esm/components/DateSelector/DateSelector.d.ts +1 -1
  103. package/esm/components/DateSelector/DateSelector.js +111 -52
  104. package/esm/components/DateSelector/DateSelector.js.map +1 -1
  105. package/esm/components/FilePreview/FilePreview.css +225 -146
  106. package/esm/components/FilePreview/FilePreview.d.ts +1 -2
  107. package/esm/components/FilePreview/FilePreview.js +21 -7
  108. package/esm/components/FilePreview/FilePreview.js.map +1 -1
  109. package/esm/components/Header/Header.css +2 -2
  110. package/esm/components/Header/Header.js +1 -1
  111. package/esm/components/Header/Header.js.map +1 -1
  112. package/esm/components/LoginDrawer/LoginDrawer.css +37 -5
  113. package/esm/components/LoginDrawer/LoginDrawer.d.ts +1 -2
  114. package/esm/components/LoginDrawer/LoginDrawer.js +2 -9
  115. package/esm/components/LoginDrawer/LoginDrawer.js.map +1 -1
  116. package/esm/components/MediaWidget/MediaItemWidget.js +2 -1
  117. package/esm/components/MediaWidget/MediaItemWidget.js.map +1 -1
  118. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js +1 -1
  119. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js.map +1 -1
  120. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.js +1 -1
  121. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.js.map +1 -1
  122. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.js +3 -0
  123. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.js.map +1 -1
  124. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js +2 -2
  125. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js.map +1 -1
  126. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +16 -7
  127. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +6 -4
  128. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -1
  129. package/esm/components/MemoriWidget/MemoriWidget.css +11 -2
  130. package/esm/components/MemoriWidget/MemoriWidget.js +105 -25
  131. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  132. package/esm/components/MicrophoneButton/MicrophoneButton.css +2 -2
  133. package/esm/components/StartPanel/StartPanel.css +8 -0
  134. package/esm/components/UploadButton/UploadButton.css +20 -17
  135. package/esm/components/UploadButton/UploadButton.js +219 -88
  136. package/esm/components/UploadButton/UploadButton.js.map +1 -1
  137. package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js +14 -4
  138. package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
  139. package/esm/components/UploadButton/UploadImages/UploadImages.js +143 -16
  140. package/esm/components/UploadButton/UploadImages/UploadImages.js.map +1 -1
  141. package/esm/components/layouts/chat.css +1 -1
  142. package/esm/components/ui/Drawer.css +8 -0
  143. package/esm/components/ui/Drawer.d.ts +2 -0
  144. package/esm/components/ui/Drawer.js +2 -2
  145. package/esm/components/ui/Drawer.js.map +1 -1
  146. package/esm/components/ui/Tooltip.css +49 -1
  147. package/esm/components/ui/Tooltip.d.ts +1 -1
  148. package/esm/helpers/constants.d.ts +1 -0
  149. package/esm/helpers/constants.js +1 -0
  150. package/esm/helpers/constants.js.map +1 -1
  151. package/esm/helpers/imageCompression.d.ts +7 -0
  152. package/esm/helpers/imageCompression.js +119 -0
  153. package/esm/helpers/imageCompression.js.map +1 -0
  154. package/esm/locales/de.json +13 -5
  155. package/esm/locales/en.json +17 -6
  156. package/esm/locales/es.json +13 -5
  157. package/esm/locales/fr.json +12 -5
  158. package/esm/locales/it.json +16 -6
  159. package/esm/styles.css +4 -4
  160. package/package.json +2 -2
  161. package/src/components/AgeVerificationModal/AgeVerificationModal.css +41 -14
  162. package/src/components/AgeVerificationModal/AgeVerificationModal.tsx +3 -1
  163. package/src/components/Auth/Auth.tsx +55 -11
  164. package/src/components/Avatar/Avatar.stories.tsx +3 -0
  165. package/src/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.css +2 -2
  166. package/src/components/Chat/Chat.css +37 -3
  167. package/src/components/Chat/Chat.stories.tsx +16 -2
  168. package/src/components/Chat/Chat.tsx +90 -21
  169. package/src/components/Chat/__snapshots__/Chat.test.tsx.snap +1752 -812
  170. package/src/components/ChatBubble/ChatBubble.css +87 -15
  171. package/src/components/ChatBubble/ChatBubble.stories.tsx +16 -2
  172. package/src/components/ChatBubble/ChatBubble.test.tsx +17 -0
  173. package/src/components/ChatBubble/ChatBubble.tsx +237 -33
  174. package/src/components/ChatBubble/__snapshots__/ChatBubble.test.tsx.snap +304 -8
  175. package/src/components/ChatHistoryDrawer/ChatHistory.css +5 -1
  176. package/src/components/ChatInputs/ChatInputs.css +293 -17
  177. package/src/components/ChatInputs/ChatInputs.tsx +156 -86
  178. package/src/components/ChatInputs/__snapshots__/ChatInputs.test.tsx.snap +430 -424
  179. package/src/components/ChatTextArea/ChatTextArea.css +75 -31
  180. package/src/components/ChatTextArea/ChatTextArea.test.tsx +1 -16
  181. package/src/components/ChatTextArea/ChatTextArea.tsx +51 -22
  182. package/src/components/ChatTextArea/__snapshots__/ChatTextArea.test.tsx.snap +9 -72
  183. package/src/components/DateSelector/DateSelector.css +125 -104
  184. package/src/components/DateSelector/DateSelector.stories.tsx +1 -1
  185. package/src/components/DateSelector/DateSelector.test.tsx +137 -23
  186. package/src/components/DateSelector/DateSelector.tsx +203 -177
  187. package/src/components/FilePreview/FilePreview.css +225 -146
  188. package/src/components/FilePreview/FilePreview.tsx +49 -36
  189. package/src/components/FilePreview/__snapshots__/FilePreview.test.tsx.snap +2 -2
  190. package/src/components/Header/Header.css +2 -2
  191. package/src/components/Header/Header.stories.tsx +5 -1
  192. package/src/components/Header/Header.tsx +1 -1
  193. package/src/components/Header/__snapshots__/Header.test.tsx.snap +1 -1
  194. package/src/components/LoginDrawer/LoginDrawer.css +37 -5
  195. package/src/components/LoginDrawer/LoginDrawer.stories.tsx +0 -1
  196. package/src/components/LoginDrawer/LoginDrawer.test.tsx +0 -1
  197. package/src/components/LoginDrawer/LoginDrawer.tsx +0 -19
  198. package/src/components/MediaWidget/MediaItemWidget.tsx +2 -1
  199. package/src/components/MemoriArtifactSystem/ArtifactDrawer.stories.tsx +996 -204
  200. package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.tsx +2 -2
  201. package/src/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyButtonWithDropdown.tsx +1 -1
  202. package/src/components/MemoriArtifactSystem/components/ArtifactActions/components/CopyMenuItem.tsx +3 -0
  203. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.tsx +56 -54
  204. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +16 -7
  205. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.tsx +12 -3
  206. package/src/components/MemoriWidget/MemoriWidget.css +11 -2
  207. package/src/components/MemoriWidget/MemoriWidget.stories.tsx +6 -3
  208. package/src/components/MemoriWidget/MemoriWidget.tsx +173 -49
  209. package/src/components/MicrophoneButton/MicrophoneButton.css +2 -2
  210. package/src/components/StartPanel/StartPanel.css +8 -0
  211. package/src/components/UploadButton/UploadButton.css +20 -17
  212. package/src/components/UploadButton/UploadButton.stories.tsx +247 -35
  213. package/src/components/UploadButton/UploadButton.tsx +280 -173
  214. package/src/components/UploadButton/UploadDocuments/UploadDocuments.tsx +19 -4
  215. package/src/components/UploadButton/UploadImages/UploadImages.tsx +196 -35
  216. package/src/components/UploadButton/__snapshots__/UploadButton.test.tsx.snap +10 -1
  217. package/src/components/layouts/FullBody/FullBody.stories.tsx +9 -10
  218. package/src/components/layouts/Totem/Totem.stories.tsx +8 -9
  219. package/src/components/layouts/ZoomedFullBody/ZoomedFullBody.stories.tsx +8 -9
  220. package/src/components/layouts/chat.css +1 -1
  221. package/src/components/layouts/layouts.stories.tsx +10 -9
  222. package/src/components/ui/Drawer.css +8 -0
  223. package/src/components/ui/Drawer.tsx +16 -12
  224. package/src/components/ui/Tooltip.css +49 -1
  225. package/src/components/ui/Tooltip.tsx +1 -1
  226. package/src/helpers/constants.ts +1 -1
  227. package/src/helpers/imageCompression.ts +230 -0
  228. package/src/index.stories.tsx +18 -0
  229. package/src/locales/de.json +13 -5
  230. package/src/locales/en.json +17 -6
  231. package/src/locales/es.json +13 -5
  232. package/src/locales/fr.json +12 -5
  233. package/src/locales/it.json +16 -6
  234. package/src/mocks/data.ts +4 -2
  235. package/src/styles.css +4 -4
  236. package/src/components/SignupForm/SignupForm.test.tsx +0 -40
  237. package/src/components/SignupForm/SignupForm.tsx +0 -457
  238. package/src/components/SignupForm/__snapshots__/SignupForm.test.tsx.snap +0 -247
  239. package/src/components/UploadMenu/UploadMenu.css +0 -47
  240. package/src/components/UploadMenu/UploadMenu.stories.tsx +0 -66
  241. package/src/components/UploadMenu/UploadMenu.test.tsx +0 -34
  242. package/src/components/UploadMenu/UploadMenu.tsx +0 -68
  243. package/src/components/UploadMenu/__snapshots__/UploadMenu.test.tsx.snap +0 -137
@@ -14,13 +14,49 @@
14
14
  background: #c5c6d1;
15
15
  }
16
16
 
17
+ .memori-chat--bubble-status-message {
18
+ display: flex;
19
+ width: 100%;
20
+ align-items: center;
21
+ justify-content: center;
22
+ padding: 0 1rem;
23
+ margin: 1.5rem auto;
24
+ }
25
+
26
+ .memori-chat--bubble-status-message-content {
27
+ display: inline-flex;
28
+ max-width: 90%;
29
+ align-items: center;
30
+ justify-content: center;
31
+ padding: 0.5rem 1rem;
32
+ border: 1px solid rgba(76, 111, 255, 0.2);
33
+ border-radius: 20px;
34
+ backdrop-filter: blur(10px);
35
+ background: linear-gradient(135deg, rgba(76, 111, 255, 0.1) 0%, rgba(76, 111, 255, 0.05) 100%);
36
+ box-shadow: 0 2px 8px rgba(76, 111, 255, 0.1);
37
+ color: var(--memori-primary, #4c6fff);
38
+ font-size: 0.75rem;
39
+ font-weight: 500;
40
+ letter-spacing: 0.02em;
41
+ line-height: 1.4;
42
+ text-align: center;
43
+ word-wrap: break-word;
44
+ }
45
+
46
+ .memori-chat--bubble-status-message-error {
47
+ border: 1px solid rgba(239, 68, 68, 0.3) !important;
48
+ background: linear-gradient(135deg, rgba(239, 68, 68, 0.1) 0%, rgba(239, 68, 68, 0.05) 100%) !important;
49
+ box-shadow: 0 2px 8px rgba(239, 68, 68, 0.15) !important;
50
+ color: rgb(239, 68, 68) !important;
51
+ }
52
+
17
53
  .memori-chat--bubble {
18
54
  display: inline-flex;
19
55
  max-width: 90%;
20
56
  flex-direction: column;
21
57
  align-items: flex-start;
22
58
  padding: 10px 16px;
23
- border-radius: 16px 16px 16px 0;
59
+ border-radius: 12px 12px 12px 0;
24
60
  margin: 10px auto 5px 10px;
25
61
  margin-bottom: 5px;
26
62
  background: var(--memori-chat-bubble-bg, #ffffff60);
@@ -30,10 +66,30 @@
30
66
  line-height: 1.33;
31
67
  }
32
68
 
69
+
70
+
33
71
  .memori-chat--bubble-container.memori-chat--with-addon .memori-chat--bubble {
34
72
  min-width: 9rem;
35
73
  }
36
74
 
75
+
76
+ .memori-chat--bubble-avatar svg {
77
+ width: 40px;
78
+ height: 40px;
79
+ padding: 0.33rem;
80
+ fill: #666;
81
+ font-size: 1.5rem;
82
+ }
83
+
84
+
85
+ .memori-chat--bubble-action-icon--from-user > span > svg {
86
+ color: #fff !important;
87
+ }
88
+
89
+ .memori-chat--bubble-action-icon--from-user:focus {
90
+ border-color: #fff !important;
91
+ }
92
+
37
93
  .memori-chat--bubble #wave {
38
94
  position: relative;
39
95
  width: 50px;
@@ -119,7 +175,7 @@
119
175
  }
120
176
 
121
177
  .memori-chat--bubble.memori-chat--user-bubble {
122
- border-radius: 16px 16px 0 16px;
178
+ border-radius: 12px 12px 0 12px;
123
179
  margin: 10px 10px 5px auto;
124
180
  background: var(--memori-chat-user-bubble-bg);
125
181
  box-shadow: 0 1px 6px 0 #d1d3de, 0 1px 3px 0 #c5c6d1;
@@ -145,14 +201,6 @@
145
201
  object-fit: cover;
146
202
  }
147
203
 
148
- .memori-chat--bubble-avatar svg {
149
- width: 40px;
150
- height: 40px;
151
- padding: 0.33rem;
152
- fill: #666;
153
- font-size: 1.5rem;
154
- }
155
-
156
204
  .memori-chat--bubble-addon {
157
205
  display: flex;
158
206
  align-items: center;
@@ -164,14 +212,10 @@
164
212
  .memori-chat--bubble .memori-chat--bubble-action-icon {
165
213
  display: flex;
166
214
  justify-content: flex-end;
167
- padding: 0;
215
+ padding: 2px;
168
216
  text-align: right;
169
217
  }
170
218
 
171
- .memori-chat--bubble .memori-chat--bubble-action-icon.memori-chat--bubble-action-icon--ai {
172
- padding: 3px;
173
- }
174
-
175
219
  .memori-chat--bubble .memori-chat--bubble-action-icon svg {
176
220
  overflow: visible;
177
221
  width: 1rem;
@@ -179,6 +223,21 @@
179
223
  color: #000;
180
224
  }
181
225
 
226
+ .memori-chat--bubble .memori-chat--bubble-action-icon.memori-chat--bubble-action-icon--copied {
227
+ border-color: var(--memori-primary, #4c6fff) !important;
228
+ background-color: rgba(76, 111, 255, 0.08);
229
+ }
230
+
231
+ .memori-chat--bubble .memori-chat--bubble-action-icon.memori-chat--bubble-action-icon--copied svg {
232
+ color: var(--memori-primary, #4c6fff);
233
+ }
234
+
235
+ .memori-chat--bubble .memori-chat--bubble-action-icon.memori-chat--bubble-action-icon--ai {
236
+ padding: 3px;
237
+ }
238
+
239
+
240
+
182
241
  .memori-chat--bubble .memori-chat--bubble-action-icon.memori-chat--bubble-action-icon--debug {
183
242
  color: var(--memori-error-color);
184
243
  }
@@ -212,6 +271,19 @@
212
271
  padding: 2.5px;
213
272
  }
214
273
 
274
+ .memori-chat--bubble-action-feedback {
275
+ margin-left: 0.35rem;
276
+ color: var(--memori-primary, #4c6fff);
277
+ font-size: 0.65rem;
278
+ font-weight: 600;
279
+ letter-spacing: 0.03em;
280
+ text-transform: uppercase;
281
+ }
282
+
283
+ .memori-chat--bubble-action-feedback--from-user {
284
+ color: var(--memori-primary-text, #fff);
285
+ }
286
+
215
287
  .memori-chat--bubble-addon .memori-chat--feedback button .memori-button--icon svg {
216
288
  width: 0.9rem;
217
289
  height: 0.9rem;
@@ -201,10 +201,10 @@ WithAllAddonsContents.args = {
201
201
  tenant,
202
202
  message: {
203
203
  fromUser: false,
204
- text: 'Proin libero ante, dignissim sit amet turpis a, pretium condimentum dolor.',
204
+ text: 'Proin libero ante.',
205
205
  initial: false,
206
206
  translatedText:
207
- 'Proin libero ante, dignissim sit amet turpis a, pretium condimentum dolor.',
207
+ 'Proin libero ter.',
208
208
  generatedByAI: true,
209
209
  },
210
210
  showFeedback: true,
@@ -314,6 +314,20 @@ FromUserWithCustomAvatarElement.args = {
314
314
  },
315
315
  };
316
316
 
317
+ export const FromUserWithCopyButtons = Template.bind({});
318
+ FromUserWithCopyButtons.args = {
319
+ memori,
320
+ tenant,
321
+ message: {
322
+ fromUser: true,
323
+ text: '<p>Questo messaggio mostra i pulsanti di copia anche per gli utenti finali.</p>',
324
+ initial: false,
325
+ translatedText:
326
+ 'Questo messaggio mostra i pulsanti di copia anche per gli utenti finali.',
327
+ },
328
+ showCopyButton: true,
329
+ };
330
+
317
331
  export const FromUserWithAvatarAndCustomAvatar = Template.bind({});
318
332
  FromUserWithAvatarAndCustomAvatar.args = {
319
333
  memori,
@@ -57,6 +57,23 @@ it('renders ChatBubble with user msg unchanged', () => {
57
57
  expect(container).toMatchSnapshot();
58
58
  });
59
59
 
60
+ it('shows copy button for user messages', () => {
61
+ const { container } = render(
62
+ <ChatBubble
63
+ memori={memori}
64
+ tenant={tenant}
65
+ sessionID={sessionID}
66
+ message={{
67
+ fromUser: true,
68
+ text: 'Mostra i pulsanti copia sul messaggio utente.',
69
+ initial: false,
70
+ }}
71
+ />
72
+ );
73
+
74
+ expect(container).toMatchSnapshot();
75
+ });
76
+
60
77
  it('renders ChatBubble with copy disabled unchanged', () => {
61
78
  const { container } = render(
62
79
  <ChatBubble
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useLayoutEffect, useState } from 'react';
1
+ import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
2
2
  import cx from 'classnames';
3
3
  import {
4
4
  ExpertReference,
@@ -89,6 +89,17 @@ const ChatBubble: React.FC<Props> = ({
89
89
  const lang = i18n.language || 'en';
90
90
  const [showingWhyThisAnswer, setShowingWhyThisAnswer] = useState(false);
91
91
  const [openFunctionCache, setOpenFunctionCache] = useState(false);
92
+ const [copyFeedback, setCopyFeedback] = useState({
93
+ plain: false,
94
+ raw: false,
95
+ });
96
+ const copyFeedbackTimers = useRef<{
97
+ plain: ReturnType<typeof setTimeout> | null;
98
+ raw: ReturnType<typeof setTimeout> | null;
99
+ }>({
100
+ plain: null,
101
+ raw: null,
102
+ });
92
103
  // Initialize MathJax on component mount
93
104
  useEffect(() => {
94
105
  if (typeof window !== 'undefined' && !window.MathJax) {
@@ -110,6 +121,17 @@ const ChatBubble: React.FC<Props> = ({
110
121
  const plainText = message.fromUser
111
122
  ? truncateMessage(cleanText)
112
123
  : stripHTML(stripOutputTags(renderedText));
124
+ const copyText = message.fromUser ? cleanText : plainText;
125
+ const shouldShowCopyButtons =
126
+ showCopyButton && (!!plainText?.length || !!message.text?.length);
127
+ const shouldShowCopyRawButton =
128
+ shouldShowCopyButtons &&
129
+ !!message.text?.length &&
130
+ plainText !== message.text;
131
+ const rawMessageText = message.fromUser
132
+ ? message.text || ''
133
+ : (message.text || '').replaceAll(/<think.*?>(.*?)<\/think>/gs, '');
134
+ const copiedLabel = t('copied') || 'Copied';
113
135
 
114
136
  // Format function cache content
115
137
  const functionCacheData = message.media?.filter(
@@ -157,11 +179,152 @@ const ChatBubble: React.FC<Props> = ({
157
179
  }
158
180
  }, [cleanText, message.fromUser, renderedText]);
159
181
 
182
+ useEffect(() => {
183
+ return () => {
184
+ (
185
+ Object.keys(copyFeedbackTimers.current) as Array<'plain' | 'raw'>
186
+ ).forEach(key => {
187
+ const timer = copyFeedbackTimers.current[key];
188
+ if (timer) {
189
+ clearTimeout(timer);
190
+ copyFeedbackTimers.current[key] = null;
191
+ }
192
+ });
193
+ };
194
+ }, []);
195
+
196
+ const triggerCopyFeedback = (type: 'plain' | 'raw') => {
197
+ setCopyFeedback(prev => ({ ...prev, [type]: true }));
198
+ if (copyFeedbackTimers.current[type]) {
199
+ clearTimeout(copyFeedbackTimers.current[type]!);
200
+ }
201
+ copyFeedbackTimers.current[type] = setTimeout(() => {
202
+ setCopyFeedback(prev => ({ ...prev, [type]: false }));
203
+ copyFeedbackTimers.current[type] = null;
204
+ }, 1500);
205
+ };
206
+
207
+ const handleCopyClick = (type: 'plain' | 'raw', text: string) => {
208
+ if (!text?.length) return;
209
+ if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
210
+ navigator.clipboard
211
+ .writeText(text)
212
+ .then(() => triggerCopyFeedback(type))
213
+ .catch(err => {
214
+ console.error('Copy failed', err);
215
+ });
216
+ } else {
217
+ triggerCopyFeedback(type);
218
+ }
219
+ };
220
+
221
+ // Check if initial is a string (status message) or boolean (legacy)
222
+ const initialStatus =
223
+ typeof message.initial === 'string' ? message.initial : null;
224
+ const showInitialDivider =
225
+ (message.initial === true || isFirst) && !initialStatus;
226
+
227
+ // Check if this is a system error message
228
+ const isSystemError = message.emitter === 'system';
229
+
230
+ if (initialStatus) {
231
+ return (
232
+ <div className="memori-chat--bubble-status-message">
233
+ <div className="memori-chat--bubble-status-message-content">
234
+ {initialStatus}
235
+ </div>
236
+ </div>
237
+ );
238
+ }
239
+
240
+ // Render system error messages as red warning messages
241
+ if (isSystemError) {
242
+ return (
243
+ <Transition
244
+ show
245
+ appear
246
+ as="div"
247
+ className={cx('memori-chat--bubble-container memori-chat-scroll-item', {
248
+ 'memori-chat--bubble-from-user': false,
249
+ })}
250
+ >
251
+ <Transition.Child
252
+ as="picture"
253
+ className="memori-chat--bubble-avatar"
254
+ enter="transition ease-in-out duration-300"
255
+ enterFrom={`opacity-0 scale-075 translate-x--15`}
256
+ enterTo="opacity-1 scale-1 translate-x-0"
257
+ leave="transition ease-in-out duration-300"
258
+ leaveFrom="opacity-1 scale-1 translate-x-0"
259
+ leaveTo={`opacity-0 scale-075 translate-x--15`}
260
+ title={
261
+ !!message.emitter?.length && !!memori.enableBoardOfExperts
262
+ ? message.emitter
263
+ : memori.name
264
+ }
265
+ >
266
+ <img
267
+ className="memori-chat--bubble-avatar-img"
268
+ alt={
269
+ !!message.emitter?.length && !!memori.enableBoardOfExperts
270
+ ? message.emitter
271
+ : memori.name
272
+ }
273
+ src={
274
+ !!message.emitter?.length &&
275
+ !!memori.enableBoardOfExperts &&
276
+ experts?.find(e => e.name === message.emitter)
277
+ ? `${
278
+ new URL(apiUrl ?? '/').origin
279
+ }/api/v1/memoriai/memori/avatar/${
280
+ experts.find(e => e.name === message.emitter)
281
+ ?.expertMemoriID
282
+ }`
283
+ : memori.avatarURL && memori.avatarURL.length > 0
284
+ ? getResourceUrl({
285
+ type: 'avatar',
286
+ tenantID: tenant?.name,
287
+ resourceURI: memori.avatarURL,
288
+ baseURL: baseUrl,
289
+ apiURL: apiUrl,
290
+ })
291
+ : getResourceUrl({
292
+ tenantID: tenant?.name,
293
+ type: 'avatar',
294
+ baseURL: baseUrl || 'https://www.aisuru.com',
295
+ apiURL: apiUrl,
296
+ })
297
+ }
298
+ onError={e => {
299
+ // Fallback image handling if primary source fails
300
+ e.currentTarget.src =
301
+ memori.avatarURL && memori.avatarURL.length > 0
302
+ ? getResourceUrl({
303
+ type: 'avatar',
304
+ tenantID: tenant?.name,
305
+ resourceURI: memori.avatarURL,
306
+ baseURL: baseUrl,
307
+ })
308
+ : getResourceUrl({
309
+ tenantID: tenant?.name,
310
+ type: 'avatar',
311
+ baseURL: baseUrl,
312
+ });
313
+
314
+ e.currentTarget.onerror = null;
315
+ }}
316
+ />
317
+ </Transition.Child>
318
+ <div className="memori-chat--bubble memori-chat--bubble-status-message-error">
319
+ <div className="memori-chat--bubble-message ">{cleanText}</div>
320
+ </div>
321
+ </Transition>
322
+ );
323
+ }
324
+
160
325
  return (
161
326
  <>
162
- {(message.initial || isFirst) && (
163
- <div className="memori-chat--bubble-initial" />
164
- )}
327
+ {showInitialDivider && <div className="memori-chat--bubble-initial" />}
165
328
  <Transition
166
329
  show
167
330
  appear
@@ -252,7 +415,7 @@ const ChatBubble: React.FC<Props> = ({
252
415
  className={cx('memori-chat--bubble', {
253
416
  'memori-chat--user-bubble': !!message.fromUser,
254
417
  'memori-chat--with-addon':
255
- (!message.fromUser && showCopyButton) ||
418
+ shouldShowCopyButtons ||
256
419
  (message.generatedByAI && showAIicon) ||
257
420
  (showFeedback && simulateUserPrompt),
258
421
  'memori-chat--ai-generated': message.generatedByAI && showAIicon,
@@ -288,42 +451,83 @@ const ChatBubble: React.FC<Props> = ({
288
451
  />
289
452
  )}
290
453
 
291
- {((!message.fromUser && showCopyButton) ||
454
+ {(shouldShowCopyButtons ||
292
455
  (message.generatedByAI && showAIicon) ||
293
456
  (message.generatedByAI && showFunctionCache) ||
294
457
  (showFeedback && simulateUserPrompt)) && (
295
458
  <div className="memori-chat--bubble-addon">
296
- {!message.fromUser && showCopyButton && (
459
+ {shouldShowCopyButtons && (
297
460
  <Button
298
461
  ghost
299
462
  shape="circle"
300
- title={t('copy') || 'Copy'}
301
- className="memori-chat--bubble-action-icon"
302
- icon={<Copy aria-label={t('copy') || 'Copy'} />}
303
- onClick={() => navigator.clipboard.writeText(plainText)}
463
+ title={copyFeedback.plain ? copiedLabel : t('copy') || 'Copy'}
464
+ className={cx('memori-chat--bubble-action-icon', {
465
+ 'memori-chat--bubble-action-icon--from-user':
466
+ message.fromUser,
467
+ 'memori-chat--bubble-action-icon--copied':
468
+ copyFeedback.plain,
469
+ })}
470
+ icon={
471
+ <Copy
472
+ aria-label={
473
+ copyFeedback.plain ? copiedLabel : t('copy') || 'Copy'
474
+ }
475
+ />
476
+ }
477
+ onClick={() => handleCopyClick('plain', copyText)}
304
478
  />
305
479
  )}
480
+ {copyFeedback.plain && (
481
+ <span
482
+ role="status"
483
+ aria-live="polite"
484
+ className={cx('memori-chat--bubble-action-feedback', {
485
+ 'memori-chat--bubble-action-feedback--from-user':
486
+ message.fromUser,
487
+ })}
488
+ >
489
+ {copiedLabel}
490
+ </span>
491
+ )}
306
492
 
307
- {!message.fromUser &&
308
- showCopyButton &&
309
- plainText !== message.text && (
310
- <Button
311
- ghost
312
- shape="circle"
313
- title={t('copyRawCode') || 'Copy raw code'}
314
- className="memori-chat--bubble-action-icon"
315
- icon={
316
- <Code aria-label={t('copyRawCode') || 'Copy raw code'} />
317
- }
318
- onClick={() => {
319
- const text = message.text .replaceAll(
320
- /<think.*?>(.*?)<\/think>/gs,
321
- ''
322
- )
323
- navigator.clipboard.writeText(text);
324
- }}
325
- />
326
- )}
493
+ {shouldShowCopyRawButton && (
494
+ <Button
495
+ ghost
496
+ shape="circle"
497
+ title={
498
+ copyFeedback.raw
499
+ ? copiedLabel
500
+ : t('copyRawCode') || 'Copy raw code'
501
+ }
502
+ className={cx('memori-chat--bubble-action-icon', {
503
+ 'memori-chat--bubble-action-icon--from-user':
504
+ message.fromUser,
505
+ 'memori-chat--bubble-action-icon--copied': copyFeedback.raw,
506
+ })}
507
+ icon={
508
+ <Code
509
+ aria-label={
510
+ copyFeedback.raw
511
+ ? copiedLabel
512
+ : t('copyRawCode') || 'Copy raw code'
513
+ }
514
+ />
515
+ }
516
+ onClick={() => handleCopyClick('raw', rawMessageText)}
517
+ />
518
+ )}
519
+ {copyFeedback.raw && (
520
+ <span
521
+ role="status"
522
+ aria-live="polite"
523
+ className={cx('memori-chat--bubble-action-feedback', {
524
+ 'memori-chat--bubble-action-feedback--from-user':
525
+ message.fromUser,
526
+ })}
527
+ >
528
+ {copiedLabel}
529
+ </span>
530
+ )}
327
531
 
328
532
  {!message.fromUser &&
329
533
  showFunctionCache &&
@@ -355,7 +559,7 @@ const ChatBubble: React.FC<Props> = ({
355
559
 
356
560
  {message.generatedByAI && showAIicon && (
357
561
  <Tooltip
358
- align="left"
562
+ align="right"
359
563
  content={
360
564
  t('generatedByAI') ||
361
565
  (lang === 'it'
@@ -381,7 +585,7 @@ const ChatBubble: React.FC<Props> = ({
381
585
  message.translatedText &&
382
586
  message.translatedText !== message.text && (
383
587
  <Tooltip
384
- align="left"
588
+ align="right"
385
589
  content={`${
386
590
  lang === 'it' ? 'Testo originale' : 'Original text'
387
591
  }: ${message.text}`}