@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.
- package/CHANGELOG.md +27 -0
- package/dist/components/Chat/Chat.js +8 -1
- package/dist/components/Chat/Chat.js.map +1 -1
- package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +160 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.d.ts +4 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js +166 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js.map +1 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +877 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.d.ts +3 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js +115 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js.map +1 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +238 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.d.ts +4 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js +104 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js.map +1 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.css +319 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.d.ts +4 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.js +50 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.js.map +1 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +343 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.d.ts +4 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +78 -0
- package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -0
- package/dist/components/MemoriArtifactSystem/context/ArtifactSystemContext.d.ts +12 -0
- package/dist/components/MemoriArtifactSystem/context/ArtifactSystemContext.js +22 -0
- package/dist/components/MemoriArtifactSystem/context/ArtifactSystemContext.js.map +1 -0
- package/dist/components/MemoriArtifactSystem/hooks/useArtifactSystem.d.ts +12 -0
- package/dist/components/MemoriArtifactSystem/hooks/useArtifactSystem.js +288 -0
- package/dist/components/MemoriArtifactSystem/hooks/useArtifactSystem.js.map +1 -0
- package/dist/components/MemoriArtifactSystem/index.d.ts +9 -0
- package/dist/components/MemoriArtifactSystem/index.js +28 -0
- package/dist/components/MemoriArtifactSystem/index.js.map +1 -0
- package/dist/components/MemoriArtifactSystem/types/artifact.types.d.ts +108 -0
- package/dist/components/MemoriArtifactSystem/types/artifact.types.js +31 -0
- package/dist/components/MemoriArtifactSystem/types/artifact.types.js.map +1 -0
- package/dist/components/icons/Print.d.ts +6 -0
- package/dist/components/icons/Print.js +6 -0
- package/dist/components/icons/Print.js.map +1 -0
- package/dist/components/layouts/Chat.js +29 -1
- package/dist/components/layouts/Chat.js.map +1 -1
- package/dist/components/layouts/FullPage.js +33 -1
- package/dist/components/layouts/FullPage.js.map +1 -1
- package/dist/components/layouts/ZoomedFullBody.js +29 -2
- package/dist/components/layouts/ZoomedFullBody.js.map +1 -1
- package/dist/components/layouts/chat.css +335 -13
- package/dist/components/layouts/zoomed-full-body.css +1 -3
- package/dist/helpers/message.js +1 -0
- package/dist/helpers/message.js.map +1 -1
- package/dist/helpers/stt/useSTT.js +76 -9
- package/dist/helpers/stt/useSTT.js.map +1 -1
- package/dist/index.js +58 -15
- package/dist/index.js.map +1 -1
- package/dist/styles.css +5 -0
- package/esm/components/Chat/Chat.js +8 -1
- package/esm/components/Chat/Chat.js.map +1 -1
- package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +160 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.d.ts +4 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js +163 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.js.map +1 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +877 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.d.ts +3 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js +112 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.js.map +1 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +238 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.d.ts +4 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js +101 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.js.map +1 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.css +319 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.d.ts +4 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.js +47 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.js.map +1 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +343 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.d.ts +4 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +75 -0
- package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -0
- package/esm/components/MemoriArtifactSystem/context/ArtifactSystemContext.d.ts +12 -0
- package/esm/components/MemoriArtifactSystem/context/ArtifactSystemContext.js +17 -0
- package/esm/components/MemoriArtifactSystem/context/ArtifactSystemContext.js.map +1 -0
- package/esm/components/MemoriArtifactSystem/hooks/useArtifactSystem.d.ts +12 -0
- package/esm/components/MemoriArtifactSystem/hooks/useArtifactSystem.js +281 -0
- package/esm/components/MemoriArtifactSystem/hooks/useArtifactSystem.js.map +1 -0
- package/esm/components/MemoriArtifactSystem/index.d.ts +9 -0
- package/esm/components/MemoriArtifactSystem/index.js +9 -0
- package/esm/components/MemoriArtifactSystem/index.js.map +1 -0
- package/esm/components/MemoriArtifactSystem/types/artifact.types.d.ts +108 -0
- package/esm/components/MemoriArtifactSystem/types/artifact.types.js +28 -0
- package/esm/components/MemoriArtifactSystem/types/artifact.types.js.map +1 -0
- package/esm/components/icons/Print.d.ts +6 -0
- package/esm/components/icons/Print.js +4 -0
- package/esm/components/icons/Print.js.map +1 -0
- package/esm/components/layouts/Chat.js +29 -1
- package/esm/components/layouts/Chat.js.map +1 -1
- package/esm/components/layouts/FullPage.js +33 -1
- package/esm/components/layouts/FullPage.js.map +1 -1
- package/esm/components/layouts/ZoomedFullBody.js +30 -3
- package/esm/components/layouts/ZoomedFullBody.js.map +1 -1
- package/esm/components/layouts/chat.css +335 -13
- package/esm/components/layouts/zoomed-full-body.css +1 -3
- package/esm/helpers/message.js +1 -0
- package/esm/helpers/message.js.map +1 -1
- package/esm/helpers/stt/useSTT.js +76 -9
- package/esm/helpers/stt/useSTT.js.map +1 -1
- package/esm/index.js +58 -15
- package/esm/index.js.map +1 -1
- package/esm/styles.css +5 -0
- package/package.json +1 -1
- package/src/components/Avatar/Avatar.test.tsx +13 -0
- package/src/components/Chat/Chat.stories.tsx +33 -2
- package/src/components/Chat/Chat.test.tsx +340 -213
- package/src/components/Chat/Chat.tsx +27 -4
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.css +160 -0
- package/src/components/MemoriArtifactSystem/components/ArtifactActions/ArtifactActions.tsx +278 -0
- package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.css +877 -0
- package/src/components/MemoriArtifactSystem/components/ArtifactDrawer/ArtifactDrawer.tsx +308 -0
- package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.css +238 -0
- package/src/components/MemoriArtifactSystem/components/ArtifactHandler/ArtifactHandler.tsx +282 -0
- package/src/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.css +319 -0
- package/src/components/MemoriArtifactSystem/components/ArtifactHistory/ArtifactHistory.tsx +178 -0
- package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.css +343 -0
- package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.tsx +190 -0
- package/src/components/MemoriArtifactSystem/context/ArtifactSystemContext.tsx +57 -0
- package/src/components/MemoriArtifactSystem/hooks/useArtifactSystem.ts +419 -0
- package/src/components/MemoriArtifactSystem/index.ts +45 -0
- package/src/components/MemoriArtifactSystem/types/artifact.types.ts +180 -0
- package/src/components/icons/Print.tsx +34 -0
- package/src/components/layouts/Chat.test.tsx +13 -0
- package/src/components/layouts/Chat.tsx +80 -25
- package/src/components/layouts/FullPage.test.tsx +40 -11
- package/src/components/layouts/FullPage.tsx +92 -24
- package/src/components/layouts/HiddenChat.test.tsx +13 -0
- package/src/components/layouts/Totem.test.tsx +13 -0
- package/src/components/layouts/WebsiteAssistant.test.tsx +13 -0
- package/src/components/layouts/ZoomedFullBody.test.tsx +13 -0
- package/src/components/layouts/ZoomedFullBody.tsx +78 -14
- package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +252 -248
- package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +504 -496
- package/src/components/layouts/__snapshots__/ZoomedFullBody.test.tsx.snap +252 -248
- package/src/components/layouts/chat.css +335 -13
- package/src/components/layouts/layouts.stories.tsx +13 -2
- package/src/components/layouts/zoomed-full-body.css +1 -3
- package/src/helpers/message.ts +1 -0
- package/src/helpers/stt/useSTT.ts +101 -16
- package/src/index.stories.tsx +26 -22
- package/src/index.tsx +46 -0
- package/src/mocks/data.ts +258 -0
- 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
|
+
};
|