@memori.ai/memori-react 8.2.0 → 8.4.0-rc.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 (146) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/components/Chat/Chat.js +8 -1
  3. package/dist/components/Chat/Chat.js.map +1 -1
  4. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +160 -0
  5. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.d.ts +4 -0
  6. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js +166 -0
  7. package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js.map +1 -0
  8. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +877 -0
  9. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.d.ts +3 -0
  10. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js +115 -0
  11. package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js.map +1 -0
  12. package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +238 -0
  13. package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.d.ts +4 -0
  14. package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js +104 -0
  15. package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js.map +1 -0
  16. package/dist/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.css +319 -0
  17. package/dist/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.d.ts +4 -0
  18. package/dist/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.js +50 -0
  19. package/dist/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.js.map +1 -0
  20. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +343 -0
  21. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.d.ts +4 -0
  22. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +78 -0
  23. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -0
  24. package/dist/components/MemoriArtifactSystem/context/ArtifactSystemContext.d.ts +12 -0
  25. package/dist/components/MemoriArtifactSystem/context/ArtifactSystemContext.js +22 -0
  26. package/dist/components/MemoriArtifactSystem/context/ArtifactSystemContext.js.map +1 -0
  27. package/dist/components/MemoriArtifactSystem/hooks/useArtifactSystem.d.ts +12 -0
  28. package/dist/components/MemoriArtifactSystem/hooks/useArtifactSystem.js +288 -0
  29. package/dist/components/MemoriArtifactSystem/hooks/useArtifactSystem.js.map +1 -0
  30. package/dist/components/MemoriArtifactSystem/index.d.ts +9 -0
  31. package/dist/components/MemoriArtifactSystem/index.js +28 -0
  32. package/dist/components/MemoriArtifactSystem/index.js.map +1 -0
  33. package/dist/components/MemoriArtifactSystem/types/artifact.types.d.ts +108 -0
  34. package/dist/components/MemoriArtifactSystem/types/artifact.types.js +31 -0
  35. package/dist/components/MemoriArtifactSystem/types/artifact.types.js.map +1 -0
  36. package/dist/components/icons/Print.d.ts +6 -0
  37. package/dist/components/icons/Print.js +6 -0
  38. package/dist/components/icons/Print.js.map +1 -0
  39. package/dist/components/layouts/Chat.js +29 -1
  40. package/dist/components/layouts/Chat.js.map +1 -1
  41. package/dist/components/layouts/FullPage.js +33 -1
  42. package/dist/components/layouts/FullPage.js.map +1 -1
  43. package/dist/components/layouts/ZoomedFullBody.js +29 -2
  44. package/dist/components/layouts/ZoomedFullBody.js.map +1 -1
  45. package/dist/components/layouts/chat.css +335 -13
  46. package/dist/components/layouts/zoomed-full-body.css +1 -3
  47. package/dist/helpers/message.js +1 -0
  48. package/dist/helpers/message.js.map +1 -1
  49. package/dist/helpers/stt/useSTT.js +76 -9
  50. package/dist/helpers/stt/useSTT.js.map +1 -1
  51. package/dist/index.js +58 -15
  52. package/dist/index.js.map +1 -1
  53. package/dist/styles.css +5 -0
  54. package/esm/components/Chat/Chat.js +8 -1
  55. package/esm/components/Chat/Chat.js.map +1 -1
  56. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +160 -0
  57. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.d.ts +4 -0
  58. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js +163 -0
  59. package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js.map +1 -0
  60. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +877 -0
  61. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.d.ts +3 -0
  62. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js +112 -0
  63. package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js.map +1 -0
  64. package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +238 -0
  65. package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.d.ts +4 -0
  66. package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js +101 -0
  67. package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js.map +1 -0
  68. package/esm/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.css +319 -0
  69. package/esm/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.d.ts +4 -0
  70. package/esm/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.js +47 -0
  71. package/esm/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.js.map +1 -0
  72. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +343 -0
  73. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.d.ts +4 -0
  74. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +75 -0
  75. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -0
  76. package/esm/components/MemoriArtifactSystem/context/ArtifactSystemContext.d.ts +12 -0
  77. package/esm/components/MemoriArtifactSystem/context/ArtifactSystemContext.js +17 -0
  78. package/esm/components/MemoriArtifactSystem/context/ArtifactSystemContext.js.map +1 -0
  79. package/esm/components/MemoriArtifactSystem/hooks/useArtifactSystem.d.ts +12 -0
  80. package/esm/components/MemoriArtifactSystem/hooks/useArtifactSystem.js +281 -0
  81. package/esm/components/MemoriArtifactSystem/hooks/useArtifactSystem.js.map +1 -0
  82. package/esm/components/MemoriArtifactSystem/index.d.ts +9 -0
  83. package/esm/components/MemoriArtifactSystem/index.js +9 -0
  84. package/esm/components/MemoriArtifactSystem/index.js.map +1 -0
  85. package/esm/components/MemoriArtifactSystem/types/artifact.types.d.ts +108 -0
  86. package/esm/components/MemoriArtifactSystem/types/artifact.types.js +28 -0
  87. package/esm/components/MemoriArtifactSystem/types/artifact.types.js.map +1 -0
  88. package/esm/components/icons/Print.d.ts +6 -0
  89. package/esm/components/icons/Print.js +4 -0
  90. package/esm/components/icons/Print.js.map +1 -0
  91. package/esm/components/layouts/Chat.js +29 -1
  92. package/esm/components/layouts/Chat.js.map +1 -1
  93. package/esm/components/layouts/FullPage.js +33 -1
  94. package/esm/components/layouts/FullPage.js.map +1 -1
  95. package/esm/components/layouts/ZoomedFullBody.js +30 -3
  96. package/esm/components/layouts/ZoomedFullBody.js.map +1 -1
  97. package/esm/components/layouts/chat.css +335 -13
  98. package/esm/components/layouts/zoomed-full-body.css +1 -3
  99. package/esm/helpers/message.js +1 -0
  100. package/esm/helpers/message.js.map +1 -1
  101. package/esm/helpers/stt/useSTT.js +76 -9
  102. package/esm/helpers/stt/useSTT.js.map +1 -1
  103. package/esm/index.js +58 -15
  104. package/esm/index.js.map +1 -1
  105. package/esm/styles.css +5 -0
  106. package/package.json +1 -1
  107. package/src/components/Avatar/Avatar.test.tsx +13 -0
  108. package/src/components/Chat/Chat.stories.tsx +33 -2
  109. package/src/components/Chat/Chat.test.tsx +340 -213
  110. package/src/components/Chat/Chat.tsx +27 -4
  111. package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +160 -0
  112. package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.tsx +278 -0
  113. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +877 -0
  114. package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.tsx +308 -0
  115. package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +238 -0
  116. package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.tsx +282 -0
  117. package/src/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.css +319 -0
  118. package/src/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.tsx +178 -0
  119. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +343 -0
  120. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.tsx +190 -0
  121. package/src/components/MemoriArtifactSystem/context/ArtifactSystemContext.tsx +57 -0
  122. package/src/components/MemoriArtifactSystem/hooks/useArtifactSystem.ts +419 -0
  123. package/src/components/MemoriArtifactSystem/index.ts +45 -0
  124. package/src/components/MemoriArtifactSystem/types/artifact.types.ts +180 -0
  125. package/src/components/icons/Print.tsx +34 -0
  126. package/src/components/layouts/Chat.test.tsx +13 -0
  127. package/src/components/layouts/Chat.tsx +80 -25
  128. package/src/components/layouts/FullPage.test.tsx +40 -11
  129. package/src/components/layouts/FullPage.tsx +92 -24
  130. package/src/components/layouts/HiddenChat.test.tsx +13 -0
  131. package/src/components/layouts/Totem.test.tsx +13 -0
  132. package/src/components/layouts/WebsiteAssistant.test.tsx +13 -0
  133. package/src/components/layouts/ZoomedFullBody.test.tsx +13 -0
  134. package/src/components/layouts/ZoomedFullBody.tsx +78 -14
  135. package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +252 -248
  136. package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +504 -496
  137. package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +252 -248
  138. package/src/components/layouts/chat.css +335 -13
  139. package/src/components/layouts/layouts.stories.tsx +13 -2
  140. package/src/components/layouts/zoomed-full-body.css +1 -3
  141. package/src/helpers/message.ts +1 -0
  142. package/src/helpers/stt/useSTT.ts +101 -16
  143. package/src/index.stories.tsx +26 -22
  144. package/src/index.tsx +46 -0
  145. package/src/mocks/data.ts +258 -0
  146. package/src/styles.css +5 -0
@@ -0,0 +1,57 @@
1
+ /**
2
+ * ArtifactSystemContext
3
+ * React Context for sharing artifact system state and actions across the application
4
+ */
5
+
6
+ import React, { createContext, useContext, ReactNode } from 'react';
7
+ import {
8
+ ArtifactSystemState,
9
+ ArtifactSystemActions,
10
+ ArtifactSystemConfig,
11
+ ArtifactSystemHook
12
+ } from '../types/artifact.types';
13
+ import { useArtifactSystem } from '../hooks/useArtifactSystem';
14
+
15
+ // Context type definition
16
+ interface ArtifactSystemContextType extends ArtifactSystemHook {
17
+ // Additional context-specific properties can be added here if needed
18
+ }
19
+
20
+ // Create the context
21
+ const ArtifactSystemContext = createContext<ArtifactSystemContextType | null>(null);
22
+
23
+ // Provider props interface
24
+ interface ArtifactSystemProviderProps {
25
+ children: ReactNode;
26
+ config?: Partial<ArtifactSystemConfig>;
27
+ }
28
+
29
+ // Provider component
30
+ export const ArtifactSystemProvider: React.FC<ArtifactSystemProviderProps> = ({
31
+ children,
32
+ config = {}
33
+ }) => {
34
+ const artifactSystem = useArtifactSystem(config);
35
+
36
+ return (
37
+ <ArtifactSystemContext.Provider value={artifactSystem}>
38
+ {children}
39
+ </ArtifactSystemContext.Provider>
40
+ );
41
+ };
42
+
43
+ // Custom hook to use the artifact system context
44
+ export const useArtifactSystemContext = (): ArtifactSystemContextType => {
45
+ const context = useContext(ArtifactSystemContext);
46
+
47
+ if (!context) {
48
+ throw new Error(
49
+ 'useArtifactSystemContext must be used within an ArtifactSystemProvider'
50
+ );
51
+ }
52
+
53
+ return context;
54
+ };
55
+
56
+ // Export the context for advanced usage
57
+ export { ArtifactSystemContext };
@@ -0,0 +1,419 @@
1
+ /**
2
+ * Custom hook for managing the artifact system state and actions
3
+ * Following React best practices and the project's patterns
4
+ */
5
+
6
+ import { useState, useCallback, useRef, useEffect } from 'react';
7
+ import {
8
+ ArtifactSystemState,
9
+ ArtifactSystemActions,
10
+ ArtifactSystemConfig,
11
+ ArtifactSystemHook,
12
+ ArtifactData,
13
+ ArtifactHistoryEntry,
14
+ DEFAULT_ARTIFACT_CONFIG,
15
+ SUPPORTED_MIME_TYPES
16
+ } from '../types/artifact.types';
17
+
18
+ /**
19
+ * Custom hook for managing artifact system state and actions
20
+ */
21
+ export const useArtifactSystem = (config: Partial<ArtifactSystemConfig> = {}): ArtifactSystemHook => {
22
+ console.log('Initializing artifact system with config:', config);
23
+
24
+ const mergedConfig = { ...DEFAULT_ARTIFACT_CONFIG, ...config };
25
+
26
+ const [state, setState] = useState<ArtifactSystemState>({
27
+ history: [
28
+ ],
29
+ currentArtifact: null,
30
+ isDrawerOpen: false,
31
+ isFullscreen: false,
32
+ processedArtifacts: new Set(['artifact-1', 'artifact-2']),
33
+ artifactCounter: 2,
34
+ });
35
+
36
+ const processedArtifactsRef = useRef<Set<string>>(new Set());
37
+
38
+ // Update ref when state changes
39
+ useEffect(() => {
40
+ console.log('Updating processed artifacts ref:', state.processedArtifacts);
41
+ processedArtifactsRef.current = state.processedArtifacts;
42
+ }, [state.processedArtifacts]);
43
+
44
+ /**
45
+ * Add a new artifact to the system
46
+ */
47
+ const addArtifact = useCallback((artifact: ArtifactData) => {
48
+ console.log('Adding artifact:', artifact);
49
+ setState(prevState => {
50
+ // Check if artifact already exists
51
+ const existingIndex = prevState.history.findIndex(item => item.id === artifact.id);
52
+
53
+ let newHistory: ArtifactHistoryEntry[];
54
+ if (existingIndex >= 0) {
55
+ console.log('Updating existing artifact at index:', existingIndex);
56
+ // Update existing artifact
57
+ newHistory = [...prevState.history];
58
+ newHistory[existingIndex] = { ...artifact, isActive: false };
59
+ } else {
60
+ console.log('Adding new artifact to history');
61
+ // Add new artifact
62
+ newHistory = [
63
+ { ...artifact, isActive: false },
64
+ ...prevState.history.slice(0, (mergedConfig.maxHistoryItems || 50) - 1)
65
+ ];
66
+ }
67
+
68
+ return {
69
+ ...prevState,
70
+ history: newHistory,
71
+ artifactCounter: prevState.artifactCounter + 1,
72
+ };
73
+ });
74
+ }, [mergedConfig.maxHistoryItems]);
75
+
76
+ /**
77
+ * Select an artifact and open the drawer
78
+ */
79
+ const selectArtifact = useCallback((artifact: ArtifactData) => {
80
+ console.log('Selecting artifact:', artifact);
81
+ setState(prevState => {
82
+ const newHistory = prevState.history.map(item => ({
83
+ ...item,
84
+ isActive: item.id === artifact.id,
85
+ }));
86
+
87
+ return {
88
+ ...prevState,
89
+ currentArtifact: artifact,
90
+ isDrawerOpen: true,
91
+ history: newHistory,
92
+ };
93
+ });
94
+ }, []);
95
+
96
+ /**
97
+ * Close the artifact drawer
98
+ */
99
+ const closeDrawer = useCallback(() => {
100
+ console.log('Closing artifact drawer');
101
+ setState(prevState => ({
102
+ ...prevState,
103
+ isDrawerOpen: false,
104
+ isFullscreen: false,
105
+ currentArtifact: null,
106
+ history: prevState.history.map(item => ({ ...item, isActive: false })),
107
+ }));
108
+ }, []);
109
+
110
+ /**
111
+ * Toggle fullscreen mode
112
+ */
113
+ const toggleFullscreen = useCallback(() => {
114
+ console.log('Toggling fullscreen mode');
115
+ setState(prevState => ({
116
+ ...prevState,
117
+ isFullscreen: !prevState.isFullscreen,
118
+ }));
119
+ }, []);
120
+
121
+ /**
122
+ * Clear all artifact history
123
+ */
124
+ const clearHistory = useCallback(() => {
125
+ console.log('Clearing artifact history');
126
+ setState(prevState => ({
127
+ ...prevState,
128
+ history: [],
129
+ currentArtifact: null,
130
+ isDrawerOpen: false,
131
+ isFullscreen: false,
132
+ processedArtifacts: new Set(),
133
+ artifactCounter: 0,
134
+ }));
135
+ processedArtifactsRef.current.clear();
136
+ }, []);
137
+
138
+ /**
139
+ * Remove a specific artifact from history
140
+ */
141
+ const removeArtifact = useCallback((id: string) => {
142
+ console.log('Removing artifact:', id);
143
+ setState(prevState => {
144
+ const newHistory = prevState.history.filter(item => item.id !== id);
145
+ const isRemovingCurrent = prevState.currentArtifact?.id === id;
146
+
147
+ return {
148
+ ...prevState,
149
+ history: newHistory,
150
+ currentArtifact: isRemovingCurrent ? null : prevState.currentArtifact,
151
+ isDrawerOpen: isRemovingCurrent ? false : prevState.isDrawerOpen,
152
+ };
153
+ });
154
+ }, []);
155
+
156
+ const actions: ArtifactSystemActions = {
157
+ addArtifact,
158
+ selectArtifact,
159
+ closeDrawer,
160
+ toggleFullscreen,
161
+ clearHistory,
162
+ removeArtifact,
163
+ };
164
+
165
+ return {
166
+ state,
167
+ actions,
168
+ config: mergedConfig,
169
+ };
170
+ };
171
+
172
+ /**
173
+ * Hook for creating artifacts from content
174
+ */
175
+ export const useArtifactCreator = () => {
176
+ const generateId = useCallback((content: string, mimeType: string): string => {
177
+ console.log('Generating ID for content type:', mimeType);
178
+ const timestamp = Date.now();
179
+ const hash = content.substring(0, 100).split('').reduce((a, b) => {
180
+ a = ((a << 5) - a) + b.charCodeAt(0);
181
+ return a & a;
182
+ }, 0);
183
+ return `artifact-${mimeType}-${Math.abs(hash)}-${timestamp}`;
184
+ }, []);
185
+
186
+ const createArtifact = useCallback((
187
+ content: string,
188
+ mimeType: string,
189
+ customTitle?: string,
190
+ messageID?: string
191
+ ): ArtifactData => {
192
+ console.log('Creating artifact:', { mimeType, customTitle, messageID });
193
+ const typeInfo = SUPPORTED_MIME_TYPES[mimeType as keyof typeof SUPPORTED_MIME_TYPES] || {
194
+ name: mimeType.toUpperCase(),
195
+ icon: '📄',
196
+ hasPreview: false,
197
+ language: 'text',
198
+ mimeType: 'text/plain',
199
+ };
200
+
201
+ const title = customTitle || `${typeInfo.icon} ${typeInfo.name} Artifact`;
202
+ const id = generateId(content, mimeType);
203
+
204
+ return {
205
+ id,
206
+ content,
207
+ mimeType: mimeType as any,
208
+ typeInfo,
209
+ title,
210
+ customTitle,
211
+ messageID: messageID || '',
212
+ timestamp: new Date(),
213
+ size: content.length,
214
+ };
215
+ }, [generateId]);
216
+
217
+ return { createArtifact };
218
+ };
219
+
220
+ /**
221
+ * Hook for processing artifact content from messages
222
+ */
223
+ export const useArtifactProcessor = () => {
224
+ const processedArtifacts = useRef<Set<string>>(new Set());
225
+
226
+ const generateContentHash = useCallback((content: string): string => {
227
+ console.log('Generating content hash');
228
+ let hash = 0;
229
+ const str = content.substring(0, 500);
230
+ for (let i = 0; i < str.length; i++) {
231
+ const char = str.charCodeAt(i);
232
+ hash = ((hash << 5) - hash) + char;
233
+ hash = hash & hash;
234
+ }
235
+ return hash.toString();
236
+ }, []);
237
+
238
+ const removeThinkTags = useCallback((text: string): string => {
239
+ console.log('Removing think tags from text');
240
+ return text.replace(/<think>[\s\S]*?<\/think>/gi, '');
241
+ }, []);
242
+
243
+ const isValidArtifact = useCallback((content: string, mimeType: string): boolean => {
244
+ console.log('Validating artifact:', { mimeType, contentLength: content.length });
245
+ // Minimum size check
246
+ if (content.length < 50) {
247
+ console.log('Artifact too small');
248
+ return false;
249
+ }
250
+
251
+ // HTML validation
252
+ if (mimeType === 'html') {
253
+ if (!content.includes('<!DOCTYPE') && !content.includes('<html')) {
254
+ console.log('Invalid HTML: missing DOCTYPE or html tag');
255
+ return false;
256
+ }
257
+ if (content.includes('<!DOCTYPE') && !content.includes('</html>')) {
258
+ console.log('Invalid HTML: unclosed html tag');
259
+ return false;
260
+ }
261
+ }
262
+
263
+ // JSON validation
264
+ if (mimeType === 'json') {
265
+ try {
266
+ JSON.parse(content);
267
+ } catch (e) {
268
+ console.log('Invalid JSON:', e);
269
+ return false;
270
+ }
271
+ }
272
+
273
+ return true;
274
+ }, []);
275
+
276
+ const cleanArtifactContent = useCallback((content: string, mimeType: string): string => {
277
+ console.log('Cleaning artifact content:', { mimeType });
278
+ if (!content) return '';
279
+
280
+ // Remove closing output tags
281
+ content = content.replace(/<\/output>/gi, '');
282
+
283
+ // HTML specific cleaning
284
+ if (mimeType === 'html') {
285
+ if (content.includes('<!DOCTYPE html') && !content.includes('</html>')) {
286
+ content += '\n</html>';
287
+ }
288
+ }
289
+
290
+ // JSON validation and cleaning
291
+ if (mimeType === 'json') {
292
+ try {
293
+ JSON.parse(content);
294
+ } catch (e) {
295
+ console.log('Failed to parse JSON:', e);
296
+ return '';
297
+ }
298
+ }
299
+
300
+ return content.trim();
301
+ }, []);
302
+
303
+ // Process content that may contain artifact tags and extract artifacts
304
+ const processArtifactContent = useCallback((
305
+ // The text content to process for artifacts
306
+ emission: string,
307
+ // Callback function called when an artifact is found
308
+ onArtifactFound: (content: string, mimeType: string, title?: string) => void
309
+ ): boolean => {
310
+ console.log('Processing artifact content');
311
+ // Return false if emission is empty or not a string
312
+ if (!emission || typeof emission !== 'string') return false;
313
+
314
+ // Track if any artifacts were processed
315
+ let processed = false;
316
+
317
+ // Remove any <think> tags from the emission
318
+ const cleanedEmission = removeThinkTags(emission);
319
+
320
+ // Regex to find <output> tags with class="memori-artifact" and data-mimetype
321
+ // Captures the mimetype and content between tags
322
+ const outputRegex = /<output\s+class\s*=\s*["\']memori-artifact["\'][^>]*data-mimetype\s*=\s*["\']([^"']+)["\'][^>]*>([\s\S]*?)(?:<\/output>|$)/gi;
323
+
324
+ // Array to store found artifacts with their metadata
325
+ const foundArtifacts: Array<{
326
+ fullMatch: string; // The full matched output tag
327
+ mimeType: string; // The mimetype of the artifact
328
+ content: string; // The content between tags
329
+ size: number; // Content length
330
+ extractedTitle?: string; // Optional title from data-title attribute
331
+ }> = [];
332
+
333
+ let match;
334
+ // Find all artifact output tags in the cleaned emission
335
+ while ((match = outputRegex.exec(cleanedEmission)) !== null) {
336
+ console.log('Found artifact match:', { mimeType: match[1] });
337
+ const fullTag = match[0];
338
+ const mimeType = match[1];
339
+ let content = match[2].trim();
340
+
341
+ // Look for an optional data-title attribute
342
+ const titleMatch = fullTag.match(/data-title\s*=\s*["\']([^"']+)["\']/i);
343
+ const extractedTitle = titleMatch ? titleMatch[1] : undefined;
344
+ console.log('Extracted title:', extractedTitle);
345
+
346
+ // Clean the content and validate it matches the mimetype
347
+ content = cleanArtifactContent(content, mimeType);
348
+
349
+ // Skip if content is empty or invalid
350
+ if (!content || !isValidArtifact(content, mimeType)) {
351
+ console.log('Skipping invalid artifact');
352
+ continue;
353
+ }
354
+
355
+ // Add valid artifact to found array
356
+ foundArtifacts.push({
357
+ fullMatch: match[0],
358
+ mimeType,
359
+ content,
360
+ size: content.length,
361
+ extractedTitle,
362
+ });
363
+ }
364
+
365
+ // Sort artifacts by size descending and deduplicate by mimetype
366
+ // keeping only the largest artifact of each type
367
+ foundArtifacts.sort((a, b) => b.size - a.size);
368
+ const uniqueByType = new Map<string, typeof foundArtifacts[0]>();
369
+
370
+ for (const artifact of foundArtifacts) {
371
+ if (!uniqueByType.has(artifact.mimeType)) {
372
+ uniqueByType.set(artifact.mimeType, artifact);
373
+ }
374
+ }
375
+
376
+ // Process each unique artifact that hasn't been processed before
377
+ for (const artifact of uniqueByType.values()) {
378
+ const hash = generateContentHash(artifact.fullMatch);
379
+
380
+ // Only process new artifacts we haven't seen before
381
+ if (!processedArtifacts.current.has(hash)) {
382
+ console.log('Processing new artifact:', { mimeType: artifact.mimeType, hash });
383
+ processedArtifacts.current.add(hash);
384
+ onArtifactFound(artifact.content, artifact.mimeType, artifact.extractedTitle);
385
+ processed = true;
386
+ }
387
+ }
388
+
389
+ // Return whether any new artifacts were processed
390
+ return processed;
391
+ }, [removeThinkTags, cleanArtifactContent, isValidArtifact, generateContentHash]);
392
+
393
+ return {
394
+ processArtifactContent,
395
+ processedArtifacts: processedArtifacts.current,
396
+ };
397
+ };
398
+
399
+ /**
400
+ * Hook for checking if a message contains artifacts
401
+ */
402
+ export const useArtifactDetector = () => {
403
+ const hasArtifacts = useCallback((messageText: string): boolean => {
404
+ console.log('Checking for artifacts in message');
405
+ if (!messageText || typeof messageText !== 'string') {
406
+ return false;
407
+ }
408
+
409
+ // Remove think tags first
410
+ const cleanedText = messageText.replace(/<think>[\s\S]*?<\/think>/gi, '');
411
+
412
+ // Check for artifact output tags
413
+ const outputRegex = /<output\s+class\s*=\s*["\']memori-artifact["\'][^>]*data-mimetype\s*=\s*["\']([^"']+)["\'][^>]*>([\s\S]*?)(?:<\/output>|$)/gi;
414
+
415
+ return outputRegex.test(cleanedText);
416
+ }, []);
417
+
418
+ return { hasArtifacts };
419
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * MemoriArtifactSystem - Main Export
3
+ * Complete artifact system for Memori/Aisuru
4
+ */
5
+
6
+ // Individual components
7
+ export { default as ArtifactDrawer } from './components/ArtifactDrawer/ArtifactDrawer';
8
+ export { default as ArtifactActions } from './components/ArtifactActions/ArtifactActions';
9
+ export { default as ArtifactPreview } from './components/ArtifactPreview/ArtifactPreview';
10
+ export { default as ArtifactHistory } from './components/ArtifactHistory/ArtifactHistory';
11
+ export { default as ArtifactHandler } from './components/ArtifactHandler/ArtifactHandler';
12
+
13
+ // Hooks
14
+ export { useArtifactSystem, useArtifactCreator, useArtifactProcessor } from './hooks/useArtifactSystem';
15
+
16
+ // Context
17
+ export {
18
+ ArtifactSystemProvider,
19
+ useArtifactSystemContext,
20
+ ArtifactSystemContext
21
+ } from './context/ArtifactSystemContext';
22
+
23
+ // Types
24
+ export type {
25
+ ArtifactMimeType,
26
+ ArtifactTypeInfo,
27
+ ArtifactData,
28
+ ArtifactHistoryEntry,
29
+ ArtifactActionsProps,
30
+ ArtifactPreviewProps,
31
+ ArtifactHistoryProps,
32
+ ArtifactHandlerProps,
33
+ ArtifactSystemConfig,
34
+ ArtifactSystemState,
35
+ ArtifactSystemActions,
36
+ ArtifactSystemHook,
37
+ ArtifactEvent,
38
+ MemoriNewDialogStateEvent,
39
+ ArtifactTab,
40
+ ArtifactValidationResult,
41
+ ArtifactProcessingOptions,
42
+ } from './types/artifact.types';
43
+
44
+ // Constants
45
+ export { DEFAULT_ARTIFACT_CONFIG, SUPPORTED_MIME_TYPES } from './types/artifact.types';
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Type definitions for the Memori Artifact System
3
+ * Following the project's TypeScript patterns and conventions
4
+ */
5
+
6
+ import { Message } from "@memori.ai/memori-api-client/dist/types";
7
+
8
+ export type ArtifactMimeType =
9
+ | 'html'
10
+ | 'json'
11
+ | 'markdown'
12
+ | 'css'
13
+ | 'javascript'
14
+ | 'typescript'
15
+ | 'svg'
16
+ | 'xml'
17
+ | 'text'
18
+ | 'python'
19
+ | 'java'
20
+ | 'cpp'
21
+ | 'csharp'
22
+ | 'php'
23
+ | 'ruby'
24
+ | 'go'
25
+ | 'rust'
26
+ | 'yaml'
27
+ | 'sql';
28
+
29
+ export interface ArtifactTypeInfo {
30
+ name: string;
31
+ icon: string;
32
+ hasPreview: boolean;
33
+ language: string;
34
+ mimeType: string;
35
+ }
36
+
37
+ export interface ArtifactData {
38
+ id: string;
39
+ content: string;
40
+ mimeType: ArtifactMimeType;
41
+ typeInfo: ArtifactTypeInfo;
42
+ title: string;
43
+ customTitle?: string;
44
+ messageID: string;
45
+ timestamp: Date;
46
+ size: number;
47
+ }
48
+
49
+ export interface ArtifactHistoryEntry extends ArtifactData {
50
+ isActive?: boolean;
51
+ }
52
+
53
+
54
+ export interface ArtifactActionsProps {
55
+ artifact: ArtifactData;
56
+ onCopy: () => void;
57
+ onDownload: () => void;
58
+ onPrint: () => void;
59
+ onOpenExternal: () => void;
60
+ onToggleFullscreen: () => void;
61
+ isFullscreen: boolean;
62
+ loading?: boolean;
63
+ }
64
+
65
+ export interface ArtifactPreviewProps {
66
+ artifact: ArtifactData;
67
+ activeTab: 'code' | 'preview';
68
+ onTabChange: (tab: 'code' | 'preview') => void;
69
+ showLineNumbers?: boolean;
70
+ enableSyntaxHighlighting?: boolean;
71
+ }
72
+
73
+ export interface ArtifactHistoryProps {
74
+ history: ArtifactHistoryEntry[];
75
+ onSelectArtifact: (artifact: ArtifactData) => void;
76
+ onClearHistory?: () => void;
77
+ maxItems?: number;
78
+ }
79
+
80
+ export interface ArtifactHandlerProps {
81
+ artifact?: ArtifactData | null;
82
+ artifacts?: ArtifactHistoryEntry[];
83
+ content?: string;
84
+ mimeType?: ArtifactMimeType;
85
+ config: ArtifactSystemConfig;
86
+ actions: ArtifactSystemActions;
87
+ message: Message;
88
+ customTitle?: string;
89
+ onArtifactCreated?: (artifact: ArtifactData) => void;
90
+ }
91
+
92
+ export interface ArtifactSystemConfig {
93
+ maxHistoryItems?: number;
94
+ enableSyntaxHighlighting?: boolean;
95
+ showLineNumbers?: boolean;
96
+ autoOpenArtifacts?: boolean;
97
+ supportedMimeTypes?: Partial<Record<ArtifactMimeType, ArtifactTypeInfo>>;
98
+ }
99
+
100
+ export interface ArtifactSystemState {
101
+ history: ArtifactHistoryEntry[];
102
+ currentArtifact: ArtifactData | null;
103
+ isDrawerOpen: boolean;
104
+ isFullscreen: boolean;
105
+ processedArtifacts: Set<string>;
106
+ artifactCounter: number;
107
+ }
108
+
109
+ export interface ArtifactSystemActions {
110
+ addArtifact: (artifact: ArtifactData) => void;
111
+ selectArtifact: (artifact: ArtifactData) => void;
112
+ closeDrawer: () => void;
113
+ toggleFullscreen: () => void;
114
+ clearHistory: () => void;
115
+ removeArtifact: (id: string) => void;
116
+ }
117
+
118
+ export interface ArtifactSystemHook {
119
+ state: ArtifactSystemState;
120
+ actions: ArtifactSystemActions;
121
+ config: ArtifactSystemConfig;
122
+ }
123
+
124
+ // Event types for artifact system
125
+ export interface ArtifactEvent {
126
+ type: 'artifact-created' | 'artifact-selected' | 'artifact-closed' | 'history-cleared';
127
+ payload: any;
128
+ }
129
+
130
+ export interface MemoriNewDialogStateEvent extends CustomEvent {
131
+ detail: {
132
+ emission?: string;
133
+ };
134
+ }
135
+
136
+ // Utility types
137
+ export type ArtifactTab = 'code' | 'preview';
138
+
139
+ export interface ArtifactValidationResult {
140
+ isValid: boolean;
141
+ errors: string[];
142
+ warnings: string[];
143
+ }
144
+
145
+ export interface ArtifactProcessingOptions {
146
+ removeThinkTags?: boolean;
147
+ validateContent?: boolean;
148
+ deduplicate?: boolean;
149
+ minContentLength?: number;
150
+ }
151
+
152
+ // Constants
153
+ export const DEFAULT_ARTIFACT_CONFIG: ArtifactSystemConfig = {
154
+ maxHistoryItems: 50,
155
+ enableSyntaxHighlighting: true,
156
+ showLineNumbers: false,
157
+ autoOpenArtifacts: true,
158
+ };
159
+
160
+ export const SUPPORTED_MIME_TYPES: Record<ArtifactMimeType, ArtifactTypeInfo> = {
161
+ html: { name: 'HTML', icon: '🌐', hasPreview: true, language: 'html', mimeType: 'application/xml' },
162
+ json: { name: 'JSON', icon: '📊', hasPreview: false, language: 'json', mimeType: 'application/json' },
163
+ markdown: { name: 'Markdown', icon: '📝', hasPreview: true, language: 'markdown', mimeType: 'text/markdown' },
164
+ css: { name: 'CSS', icon: '🎨', hasPreview: true, language: 'css', mimeType: 'text/css' },
165
+ javascript: { name: 'JavaScript', icon: '⚡', hasPreview: false, language: 'javascript', mimeType: 'text/javascript' },
166
+ typescript: { name: 'TypeScript', icon: '🔷', hasPreview: false, language: 'typescript', mimeType: 'text/typescript' },
167
+ svg: { name: 'SVG', icon: '🖼️', hasPreview: true, language: 'xml', mimeType: 'image/svg+xml' },
168
+ xml: { name: 'XML', icon: '📋', hasPreview: false, language: 'xml', mimeType: 'text/xml' },
169
+ text: { name: 'Text', icon: '📄', hasPreview: false, language: 'text', mimeType: 'text/plain' },
170
+ python: { name: 'Python', icon: '🐍', hasPreview: false, language: 'python', mimeType: 'text/x-python' },
171
+ java: { name: 'Java', icon: '☕', hasPreview: false, language: 'java', mimeType: 'text/x-java' },
172
+ cpp: { name: 'C++', icon: '⚙️', hasPreview: false, language: 'cpp', mimeType: 'text/x-c++' },
173
+ csharp: { name: 'C#', icon: '🔷', hasPreview: false, language: 'csharp', mimeType: 'text/x-csharp' },
174
+ php: { name: 'PHP', icon: '🐘', hasPreview: false, language: 'php', mimeType: 'text/x-php' },
175
+ ruby: { name: 'Ruby', icon: '💎', hasPreview: false, language: 'ruby', mimeType: 'text/x-ruby' },
176
+ go: { name: 'Go', icon: '🐹', hasPreview: false, language: 'go', mimeType: 'text/x-go' },
177
+ rust: { name: 'Rust', icon: '🦀', hasPreview: false, language: 'rust', mimeType: 'text/x-rust' },
178
+ yaml: { name: 'YAML', icon: '📝', hasPreview: false, language: 'yaml', mimeType: 'text/yaml' },
179
+ sql: { name: 'SQL', icon: '🗄️', hasPreview: false, language: 'sql', mimeType: 'text/x-sql' },
180
+ };