@memori.ai/memori-react 7.32.5 → 7.32.7

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 (57) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/components/Chat/Chat.js +9 -5
  3. package/dist/components/Chat/Chat.js.map +1 -1
  4. package/dist/components/ChatBubble/ChatBubble.d.ts +2 -0
  5. package/dist/components/ChatBubble/ChatBubble.js +20 -10
  6. package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
  7. package/dist/components/ChatHistoryDrawer/ChatHistory.js +1 -1
  8. package/dist/components/ChatHistoryDrawer/ChatHistory.js.map +1 -1
  9. package/dist/components/ChatInputs/ChatInputs.d.ts +2 -1
  10. package/dist/components/ChatInputs/ChatInputs.js +2 -4
  11. package/dist/components/ChatInputs/ChatInputs.js.map +1 -1
  12. package/dist/components/ExportHistoryButton/ExportHistoryButton.d.ts +14 -0
  13. package/dist/components/ExportHistoryButton/ExportHistoryButton.js +38 -0
  14. package/dist/components/ExportHistoryButton/ExportHistoryButton.js.map +1 -0
  15. package/dist/components/UploadButton/UploadButton.d.ts +2 -1
  16. package/dist/components/UploadButton/UploadButton.js +2 -2
  17. package/dist/components/UploadButton/UploadButton.js.map +1 -1
  18. package/dist/components/UploadButton/UploadImages/UploadImages.d.ts +2 -1
  19. package/dist/components/UploadButton/UploadImages/UploadImages.js +2 -4
  20. package/dist/components/UploadButton/UploadImages/UploadImages.js.map +1 -1
  21. package/dist/components/WhyThisAnswer/WhyThisAnswer.d.ts +3 -2
  22. package/dist/components/WhyThisAnswer/WhyThisAnswer.js +3 -5
  23. package/dist/components/WhyThisAnswer/WhyThisAnswer.js.map +1 -1
  24. package/esm/components/Chat/Chat.js +9 -5
  25. package/esm/components/Chat/Chat.js.map +1 -1
  26. package/esm/components/ChatBubble/ChatBubble.d.ts +2 -0
  27. package/esm/components/ChatBubble/ChatBubble.js +20 -10
  28. package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
  29. package/esm/components/ChatHistoryDrawer/ChatHistory.js +1 -1
  30. package/esm/components/ChatHistoryDrawer/ChatHistory.js.map +1 -1
  31. package/esm/components/ChatInputs/ChatInputs.d.ts +2 -1
  32. package/esm/components/ChatInputs/ChatInputs.js +2 -4
  33. package/esm/components/ChatInputs/ChatInputs.js.map +1 -1
  34. package/esm/components/ExportHistoryButton/ExportHistoryButton.d.ts +14 -0
  35. package/esm/components/ExportHistoryButton/ExportHistoryButton.js +35 -0
  36. package/esm/components/ExportHistoryButton/ExportHistoryButton.js.map +1 -0
  37. package/esm/components/UploadButton/UploadButton.d.ts +2 -1
  38. package/esm/components/UploadButton/UploadButton.js +2 -2
  39. package/esm/components/UploadButton/UploadButton.js.map +1 -1
  40. package/esm/components/UploadButton/UploadImages/UploadImages.d.ts +2 -1
  41. package/esm/components/UploadButton/UploadImages/UploadImages.js +2 -4
  42. package/esm/components/UploadButton/UploadImages/UploadImages.js.map +1 -1
  43. package/esm/components/WhyThisAnswer/WhyThisAnswer.d.ts +3 -2
  44. package/esm/components/WhyThisAnswer/WhyThisAnswer.js +3 -5
  45. package/esm/components/WhyThisAnswer/WhyThisAnswer.js.map +1 -1
  46. package/package.json +2 -2
  47. package/src/components/Chat/Chat.tsx +15 -3
  48. package/src/components/Chat/__snapshots__/Chat.test.tsx.snap +19 -651
  49. package/src/components/ChatBubble/ChatBubble.tsx +34 -12
  50. package/src/components/ChatBubble/__snapshots__/ChatBubble.test.tsx.snap +0 -3
  51. package/src/components/ChatHistoryDrawer/ChatHistory.tsx +1 -1
  52. package/src/components/ChatInputs/ChatInputs.tsx +4 -8
  53. package/src/components/UploadButton/UploadButton.tsx +6 -7
  54. package/src/components/UploadButton/UploadImages/UploadImages.tsx +3 -4
  55. package/src/components/WhyThisAnswer/WhyThisAnswer.stories.tsx +2 -1
  56. package/src/components/WhyThisAnswer/WhyThisAnswer.test.tsx +5 -4
  57. package/src/components/WhyThisAnswer/WhyThisAnswer.tsx +7 -6
@@ -28,6 +28,7 @@ import { renderMsg, truncateMessage } from '../../helpers/message';
28
28
  import Expandable from '../ui/Expandable';
29
29
  import Modal from '../ui/Modal';
30
30
  import MediaWidget from '../MediaWidget/MediaWidget';
31
+ import memoriApiClient from '@memori.ai/memori-api-client';
31
32
 
32
33
  // Always import and load MathJax
33
34
  import { installMathJax } from '../../helpers/utils';
@@ -48,6 +49,7 @@ export interface Props {
48
49
  tenant?: Tenant;
49
50
  baseUrl?: string;
50
51
  apiUrl?: string;
52
+ client?: ReturnType<typeof memoriApiClient>;
51
53
  showFeedback?: boolean;
52
54
  showWhyThisAnswer?: boolean;
53
55
  showCopyButton?: boolean;
@@ -68,6 +70,7 @@ const ChatBubble: React.FC<Props> = ({
68
70
  tenant,
69
71
  baseUrl,
70
72
  apiUrl,
73
+ client,
71
74
  sessionID,
72
75
  showFeedback,
73
76
  showWhyThisAnswer = true,
@@ -86,7 +89,9 @@ const ChatBubble: React.FC<Props> = ({
86
89
  const lang = i18n.language || 'en';
87
90
  const [showingWhyThisAnswer, setShowingWhyThisAnswer] = useState(false);
88
91
  const [openFunctionCache, setOpenFunctionCache] = useState(false);
89
- const [documentAttachments, setDocumentAttachments] = useState<(Medium & { type?: string })[]>([]);
92
+ const [documentAttachments, setDocumentAttachments] = useState<
93
+ (Medium & { type?: string })[]
94
+ >([]);
90
95
 
91
96
  // Initialize MathJax on component mount
92
97
  useEffect(() => {
@@ -98,14 +103,17 @@ const ChatBubble: React.FC<Props> = ({
98
103
  // Extract document attachments from text and convert them to media
99
104
  useEffect(() => {
100
105
  const text = message.translatedText || message.text;
101
- const documentAttachmentRegex = /<document_attachment filename="([^"]+)" type="([^"]+)">([\s\S]*?)<\/document_attachment>/g;
106
+ const documentAttachmentRegex =
107
+ /<document_attachment filename="([^"]+)" type="([^"]+)">([\s\S]*?)<\/document_attachment>/g;
102
108
  const attachments: (Medium & { type?: string })[] = [];
103
109
  let match;
104
110
 
105
111
  while ((match = documentAttachmentRegex.exec(text)) !== null) {
106
112
  const [, filename, type, content] = match;
107
113
  attachments.push({
108
- mediumID: `doc_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
114
+ mediumID: `doc_${Date.now()}_${Math.random()
115
+ .toString(36)
116
+ .substr(2, 9)}`,
109
117
  url: '',
110
118
  mimeType: type,
111
119
  title: filename,
@@ -137,6 +145,24 @@ const ChatBubble: React.FC<Props> = ({
137
145
  m => m.properties?.functionCache === 'true'
138
146
  );
139
147
 
148
+ // Filter out media items that are already being rendered as document attachments
149
+ // to prevent duplicate rendering
150
+ const filteredMedia =
151
+ message.media?.filter(mediaItem => {
152
+ // If this is a document attachment (has isAttachedFile property),
153
+ // don't render it in the regular media widget
154
+ if (mediaItem.properties?.isAttachedFile) {
155
+ return false;
156
+ }
157
+ return true;
158
+ }) || [];
159
+
160
+ // Filter out HTML and plain text from regular media
161
+ const regularMedia = filteredMedia.filter(
162
+ m => m.mimeType !== 'text/html' && m.mimeType !== 'text/plain'
163
+ );
164
+ const links = filteredMedia.filter(m => m.mimeType === 'text/html');
165
+
140
166
  useLayoutEffect(() => {
141
167
  if (typeof window !== 'undefined' && !message.fromUser) {
142
168
  const timer = setTimeout(() => {
@@ -178,8 +204,6 @@ const ChatBubble: React.FC<Props> = ({
178
204
  }
179
205
  }, [cleanText, message.fromUser, renderedText]);
180
206
 
181
- console.log('media',message?.media);
182
-
183
207
  return (
184
208
  <>
185
209
  {(message.initial || isFirst) && (
@@ -509,12 +533,10 @@ const ChatBubble: React.FC<Props> = ({
509
533
  )}
510
534
 
511
535
  {/* Render existing media */}
512
- {message.media && message.media.length > 0 && (
536
+ {regularMedia.length > 0 && (
513
537
  <MediaWidget
514
- media={message.media.filter(
515
- m => m.mimeType !== 'text/html' && m.mimeType !== 'text/plain'
516
- )}
517
- links={message.media.filter(m => m.mimeType === 'text/html')}
538
+ media={regularMedia}
539
+ links={links}
518
540
  sessionID={sessionID}
519
541
  baseUrl={baseUrl}
520
542
  apiUrl={apiUrl}
@@ -522,12 +544,12 @@ const ChatBubble: React.FC<Props> = ({
522
544
  />
523
545
  )}
524
546
 
525
- {showingWhyThisAnswer && apiUrl && (
547
+ {showingWhyThisAnswer && client && (
526
548
  <WhyThisAnswer
549
+ client={client}
527
550
  visible={showingWhyThisAnswer}
528
551
  message={message}
529
552
  closeDrawer={() => setShowingWhyThisAnswer(false)}
530
- apiURL={apiUrl}
531
553
  sessionID={sessionID}
532
554
  />
533
555
  )}
@@ -6803,9 +6803,6 @@ exports[`renders ChatBubble with debug button unchanged 1`] = `
6803
6803
  </div>
6804
6804
  </div>
6805
6805
  </div>
6806
- <div
6807
- class="memori-media-widget"
6808
- />
6809
6806
  </div>
6810
6807
  `;
6811
6808
 
@@ -378,7 +378,7 @@ const ChatHistoryDrawer = ({
378
378
  history,
379
379
  }: Props) => {
380
380
  const { t } = useTranslation();
381
- const { getChatLogsByUser, getSessionChatLogs } = apiClient.chatLogs;
381
+ const { getChatLogsByUser } = apiClient.chatLogs;
382
382
 
383
383
  const textCurrentChat = `${t(
384
384
  'write_and_speak.conversationStartedLabel'
@@ -19,10 +19,7 @@ export interface Props {
19
19
  setAttachmentsMenuOpen: (attachmentsMenuOpen: 'link' | 'media') => void;
20
20
  userMessage?: string;
21
21
  onChangeUserMessage: (userMessage: string) => void;
22
- sendMessage: (
23
- msg: string,
24
- media?: (Medium & { type: string })[]
25
- ) => void;
22
+ sendMessage: (msg: string, media?: (Medium & { type: string })[]) => void;
26
23
  onTextareaFocus: () => void;
27
24
  onTextareaBlur: () => void;
28
25
  resetTranscript: () => void;
@@ -36,8 +33,8 @@ export interface Props {
36
33
  authToken?: string;
37
34
  showUpload?: boolean;
38
35
  sessionID?: string;
39
- apiURL?: string;
40
36
  memoriID?: string;
37
+ client?: ReturnType<typeof memoriApiClient>;
41
38
  }
42
39
 
43
40
  const ChatInputs: React.FC<Props> = ({
@@ -58,8 +55,8 @@ const ChatInputs: React.FC<Props> = ({
58
55
  showUpload = false,
59
56
  sessionID,
60
57
  authToken,
61
- apiURL,
62
58
  memoriID,
59
+ client,
63
60
  }) => {
64
61
  const { t } = useTranslation();
65
62
 
@@ -77,7 +74,6 @@ const ChatInputs: React.FC<Props> = ({
77
74
  >([]);
78
75
 
79
76
  // Client
80
- const client = apiURL ? memoriApiClient(apiURL) : null;
81
77
  const { dialog } = client || {
82
78
  dialog: { postMediumDeselectedEvent: null },
83
79
  };
@@ -191,7 +187,7 @@ const ChatInputs: React.FC<Props> = ({
191
187
  {/* Replace the individual buttons with our unified upload component */}
192
188
  <UploadButton
193
189
  authToken={authToken}
194
- apiUrl={apiURL}
190
+ client={client}
195
191
  sessionID={sessionID}
196
192
  isMediaAccepted={dialogState?.acceptsMedia || false}
197
193
  setDocumentPreviewFiles={setDocumentPreviewFiles}
@@ -8,6 +8,7 @@ import cx from 'classnames';
8
8
  import UploadDocuments from './UploadDocuments/UploadDocuments';
9
9
  import UploadImages from './UploadImages/UploadImages';
10
10
  import { useTranslation } from 'react-i18next';
11
+ import memoriApiClient from '@memori.ai/memori-api-client';
11
12
 
12
13
  // Constants
13
14
  const MAX_IMAGES = 5;
@@ -16,7 +17,7 @@ const MAX_DOCUMENTS = 1;
16
17
  // Props interface
17
18
  interface UploadManagerProps {
18
19
  authToken?: string;
19
- apiUrl?: string;
20
+ client?: ReturnType<typeof memoriApiClient>;
20
21
  sessionID?: string;
21
22
  isMediaAccepted?: boolean;
22
23
  setDocumentPreviewFiles: any;
@@ -32,7 +33,7 @@ interface UploadManagerProps {
32
33
 
33
34
  const UploadButton: React.FC<UploadManagerProps> = ({
34
35
  authToken = '',
35
- apiUrl = '',
36
+ client,
36
37
  sessionID = '',
37
38
  isMediaAccepted = false,
38
39
  setDocumentPreviewFiles,
@@ -163,8 +164,6 @@ ${file.content}
163
164
 
164
165
  // When image option is clicked
165
166
  const handleImageClick = () => {
166
-
167
-
168
167
  if (!isMediaAccepted) {
169
168
  addError({
170
169
  message:
@@ -262,11 +261,11 @@ ${file.content}
262
261
  <div
263
262
  className={cx('memori--upload-menu-item', {
264
263
  'memori--upload-menu-item--disabled':
265
- !isMediaAccepted || hasReachedImageLimit,
264
+ !isMediaAccepted || hasReachedImageLimit,
266
265
  })}
267
266
  onClick={handleImageClick}
268
267
  title={
269
- !isMediaAccepted
268
+ !isMediaAccepted
270
269
  ? t('upload.mediaNotAccepted') ?? 'Media uploads not accepted'
271
270
  : hasReachedImageLimit
272
271
  ? t('upload.maxImagesReached', { max: MAX_IMAGES }) ??
@@ -307,7 +306,7 @@ ${file.content}
307
306
  <div className="memori--hidden-uploader" ref={imageRef}>
308
307
  <UploadImages
309
308
  authToken={authToken}
310
- apiUrl={apiUrl}
309
+ client={client}
311
310
  setDocumentPreviewFiles={setDocumentPreviewFiles}
312
311
  sessionID={sessionID}
313
312
  documentPreviewFiles={documentPreviewFiles}
@@ -34,7 +34,7 @@ const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
34
34
  // Props interface
35
35
  interface UploadImagesProps {
36
36
  authToken?: string;
37
- apiUrl?: string;
37
+ client?: ReturnType<typeof memoriApiClient>;
38
38
  sessionID?: string;
39
39
  isMediaAccepted?: boolean;
40
40
  setDocumentPreviewFiles: any;
@@ -48,8 +48,8 @@ const ALLOWED_FILE_TYPES = ['.jpg', '.jpeg', '.png'];
48
48
 
49
49
  const UploadImages: React.FC<UploadImagesProps> = ({
50
50
  authToken = '',
51
- apiUrl = '',
52
51
  sessionID = '',
52
+ client,
53
53
  isMediaAccepted = false,
54
54
  setDocumentPreviewFiles,
55
55
  documentPreviewFiles,
@@ -59,7 +59,6 @@ const UploadImages: React.FC<UploadImagesProps> = ({
59
59
  }) => {
60
60
  const { t, i18n } = useTranslation();
61
61
  // Client
62
- const client = apiUrl ? memoriApiClient(apiUrl) : null;
63
62
  const { backend, dialog } = client || {
64
63
  backend: { uploadAsset: null, uploadAssetUnlogged: null },
65
64
  dialog: { postMediumSelectedEvent: null, postMediumDeselectedEvent: null },
@@ -187,7 +186,7 @@ const UploadImages: React.FC<UploadImagesProps> = ({
187
186
  const fileDataUrl = e.target?.result as string;
188
187
  const fileId = Math.random().toString(36).substr(2, 9);
189
188
 
190
- if (apiUrl) {
189
+ if (client) {
191
190
  try {
192
191
  let asset: Asset;
193
192
  let response;
@@ -4,6 +4,7 @@ import { sessionID } from '../../mocks/data';
4
4
  import I18nWrapper from '../../I18nWrapper';
5
5
  import WhyThisAnswer, { Props } from './WhyThisAnswer';
6
6
  import { SearchMatches } from '@memori.ai/memori-api-client/dist/types';
7
+ import memoriApiClient from '@memori.ai/memori-api-client';
7
8
 
8
9
  import './WhyThisAnswer.css';
9
10
 
@@ -28,7 +29,7 @@ const Template: Story<Props> = args => (
28
29
  <I18nWrapper>
29
30
  <WhyThisAnswer
30
31
  {...args}
31
- apiURL="https://backend.memori.ai"
32
+ client={memoriApiClient()}
32
33
  sessionID={sessionID}
33
34
  message={{
34
35
  questionAnswered: 'Test message',
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { render } from '@testing-library/react';
3
3
  import WhyThisAnswer from './WhyThisAnswer';
4
4
  import { sessionID } from '../../mocks/data';
5
+ import memoriApiClient from '@memori.ai/memori-api-client';
5
6
 
6
7
  beforeEach(() => {
7
8
  // @ts-ignore
@@ -16,7 +17,7 @@ beforeEach(() => {
16
17
  it('renders WhyThisAnswer hidden unchanged', () => {
17
18
  const { container } = render(
18
19
  <WhyThisAnswer
19
- apiURL="https://backend.memori.ai"
20
+ client={memoriApiClient()}
20
21
  sessionID={sessionID}
21
22
  visible={false}
22
23
  message={{
@@ -40,7 +41,7 @@ it('renders WhyThisAnswer hidden unchanged', () => {
40
41
  it('renders WhyThisAnswer visible unchanged', () => {
41
42
  const { container } = render(
42
43
  <WhyThisAnswer
43
- apiURL="https://backend.memori.ai"
44
+ client={memoriApiClient()}
44
45
  sessionID={sessionID}
45
46
  visible={true}
46
47
  message={{
@@ -64,7 +65,7 @@ it('renders WhyThisAnswer visible unchanged', () => {
64
65
  it('renders WhyThisAnswer loading unchanged', () => {
65
66
  const { container } = render(
66
67
  <WhyThisAnswer
67
- apiURL="https://backend.memori.ai"
68
+ client={memoriApiClient()}
68
69
  sessionID={sessionID}
69
70
  visible={true}
70
71
  message={{
@@ -89,7 +90,7 @@ it('renders WhyThisAnswer loading unchanged', () => {
89
90
  it('renders WhyThisAnswer with data unchanged', () => {
90
91
  const { container } = render(
91
92
  <WhyThisAnswer
92
- apiURL="https://backend.memori.ai"
93
+ client={memoriApiClient()}
93
94
  sessionID={sessionID}
94
95
  visible={true}
95
96
  message={{
@@ -15,12 +15,12 @@ import MediaWidget from '../MediaWidget/MediaWidget';
15
15
  import Card from '../ui/Card';
16
16
 
17
17
  export interface Props {
18
- apiURL: string;
19
18
  sessionID: string;
20
19
  message: Message;
21
20
  initialMatches?: SearchMatches[];
22
21
  visible?: boolean;
23
22
  closeDrawer: () => void;
23
+ client?: ReturnType<typeof memoriApiClient>;
24
24
  _TEST_loading?: boolean;
25
25
  }
26
26
 
@@ -29,17 +29,16 @@ const addQuestionMark = (question: string) =>
29
29
 
30
30
  const WhyThisAnswer = ({
31
31
  message,
32
- apiURL,
33
32
  sessionID,
34
33
  visible = true,
35
34
  initialMatches = [],
36
35
  closeDrawer,
36
+ client,
37
37
  _TEST_loading = false,
38
38
  }: Props) => {
39
39
  const { t } = useTranslation();
40
40
 
41
- const client = memoriApiClient(apiURL);
42
- const searchMemory = client.search.searchMemory;
41
+ const searchMemory = client?.search.searchMemory;
43
42
 
44
43
  const [matches, setMatches] = useState<SearchMatches[]>(initialMatches);
45
44
  const [loading, setLoading] = useState(_TEST_loading);
@@ -50,7 +49,7 @@ const WhyThisAnswer = ({
50
49
  const fetchMemories = useCallback(async () => {
51
50
  setLoading(true);
52
51
 
53
- if (_TEST_loading) return;
52
+ if (_TEST_loading || !searchMemory) return;
54
53
 
55
54
  try {
56
55
  const { matches, ...response } = await searchMemory(sessionID, {
@@ -189,7 +188,9 @@ const WhyThisAnswer = ({
189
188
  )}
190
189
  {m.memory.answers?.map((a, i) => (
191
190
  <p key={i} className="memori--whythisanswer-answer">
192
- <Expandable mode="rows" rows={3}>{a.text}</Expandable>
191
+ <Expandable mode="rows" rows={3}>
192
+ {a.text}
193
+ </Expandable>
193
194
  </p>
194
195
  ))}
195
196