search
This commit is contained in:
parent
7a24d3cb98
commit
29f76c9e9d
@ -1,4 +1,4 @@
|
||||
# Data Identification Manager : Personal Edition
|
||||
# Data Identification Manager
|
||||
|
||||
An Electron-based desktop application that uses AI to help you search and understand your files through natural language queries.
|
||||
|
||||
|
@ -277,6 +277,28 @@ export function setupIpcHandlers() {
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('meilisearch-get-documents', async (_: unknown, indexName: string) => {
|
||||
try {
|
||||
const documents = await (global as any).meilisearchService.getDocuments(indexName);
|
||||
return { success: true, data: documents };
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
console.error('Error getting documents from Meilisearch:', err);
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('meilisearch-search', async (_: unknown, indexName: string, query: string) => {
|
||||
try {
|
||||
const results = await (global as any).meilisearchService.search(indexName, query);
|
||||
return { success: true, data: results };
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
console.error('Error searching Meilisearch:', err);
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default setupIpcHandlers;
|
||||
|
@ -14,170 +14,14 @@ const slugify = (str: string) => {
|
||||
};
|
||||
|
||||
const { platform } = require('os');
|
||||
|
||||
class MeilisearchService {
|
||||
private binaryPath: string;
|
||||
private serverProcess: any | null = null;
|
||||
private client: any | null = null;
|
||||
|
||||
constructor() {
|
||||
this.binaryPath = this.getBinaryPath();
|
||||
this.client = new MeiliSearch({
|
||||
host: 'http://127.0.0.1:7700',
|
||||
apiKey: process.env.MEILISEARCH_MASTER_KEY || 'Damie',
|
||||
});
|
||||
}
|
||||
|
||||
private getBinaryPath(): string {
|
||||
const arch = process.arch;
|
||||
let binaryName: string;
|
||||
|
||||
switch (platform()) {
|
||||
case 'darwin':
|
||||
binaryName = arch === 'arm64' ? 'meilisearch-macos-arm' : 'meilisearch-macos-x64';
|
||||
break;
|
||||
case 'win32':
|
||||
binaryName = 'meilisearch-windows.exe';
|
||||
break;
|
||||
case 'linux':
|
||||
binaryName = arch === 'arm' ? 'meilisearch-linux-arm' : 'meilisearch-linux-x64';
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported platform: ${platform()}`);
|
||||
}
|
||||
|
||||
return path.join(__dirname, '..', 'meilisearch_binaries', binaryName);
|
||||
}
|
||||
|
||||
public async startServer(): Promise<void> {
|
||||
if (this.serverProcess) {
|
||||
console.log('Meilisearch server is already running.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const apiKey = 'Damie'; // Use the system's name as the API key
|
||||
process.env.MEILISEARCH_MASTER_KEY = apiKey;
|
||||
|
||||
this.serverProcess = spawn(this.binaryPath, ['--http-addr', '127.0.0.1:7700'], {
|
||||
env: process.env, // Pass the environment variables to the child process
|
||||
});
|
||||
|
||||
this.serverProcess.stdout?.on('data', (data) => {
|
||||
console.log(`Meilisearch: ${data}`);
|
||||
});
|
||||
|
||||
this.serverProcess.stderr?.on('data', (data) => {
|
||||
console.error(`Meilisearch: ${data}`);
|
||||
});
|
||||
|
||||
this.serverProcess.on('close', (code) => {
|
||||
console.log(`Meilisearch server stopped with code ${code}`);
|
||||
this.serverProcess = null;
|
||||
});
|
||||
|
||||
this.serverProcess.on('error', (err) => {
|
||||
console.error('Failed to start Meilisearch server:', err);
|
||||
this.serverProcess = null;
|
||||
});
|
||||
|
||||
console.log('Meilisearch server started.');
|
||||
} catch (error) {
|
||||
console.error('Failed to start Meilisearch server:', error);
|
||||
}
|
||||
}
|
||||
|
||||
public async stopServer(): Promise<void> {
|
||||
if (!this.serverProcess) {
|
||||
console.log('Meilisearch server is not running.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.serverProcess.kill();
|
||||
this.serverProcess = null;
|
||||
console.log('Meilisearch server stopped.');
|
||||
}
|
||||
|
||||
// Implement methods for adding, removing, and updating documents and collections
|
||||
// using the Meilisearch API.
|
||||
public async addDocuments(indexName: string, documents: any[]): Promise<void> {
|
||||
// TODO: Implement this method
|
||||
console.log(`Adding documents to index ${indexName}`);
|
||||
}
|
||||
|
||||
public async removeDocuments(indexName: string, documentIds: string[]): Promise<void> {
|
||||
// TODO: Implement this method
|
||||
console.log(`Removing documents from index ${indexName}`);
|
||||
}
|
||||
|
||||
public async updateDocuments(indexName: string, documents: any[]): Promise<void> {
|
||||
// TODO: Implement this method
|
||||
console.log(`Updating documents in index ${indexName}`);
|
||||
}
|
||||
|
||||
public async createIndex(indexName: string): Promise<void> {
|
||||
const sluggedIndexName = slugify(indexName);
|
||||
// TODO: Implement this method
|
||||
console.log(`Creating index ${sluggedIndexName}`);
|
||||
try {
|
||||
await this.client.createIndex(sluggedIndexName);
|
||||
console.log(`Index ${sluggedIndexName} created successfully`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to create index ${sluggedIndexName}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async deleteIndex(indexName: string): Promise<void> {
|
||||
try {
|
||||
await this.client.deleteIndex(indexName);
|
||||
console.log(`Index ${indexName} deleted successfully`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to delete index ${indexName}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async addDocuments(indexName: string, documents: any[]): Promise<void> {
|
||||
try {
|
||||
const index = await this.client.index(indexName);
|
||||
await index.addDocuments(documents);
|
||||
console.log(`Documents added to index ${indexName} successfully`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to add documents to index ${indexName}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async removeDocuments(indexName: string, documentIds: string[]): Promise<void> {
|
||||
try {
|
||||
const index = await this.client.index(indexName);
|
||||
await index.deleteDocuments(documentIds);
|
||||
console.log(`Documents removed from index ${indexName} successfully`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to remove documents from index ${indexName}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async updateDocuments(indexName: string, documents: any[]): Promise<void> {
|
||||
try {
|
||||
const index = await this.client.index(indexName);
|
||||
await index.updateDocuments(documents);
|
||||
console.log(`Documents updated in index ${indexName} successfully`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to update documents in index ${indexName}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize IPC handlers immediately
|
||||
setupIpcHandlers();
|
||||
import MeilisearchService from './services/meilisearchService';
|
||||
|
||||
const meilisearchService = new MeilisearchService();
|
||||
(global as any).meilisearchService = meilisearchService;
|
||||
|
||||
// Initialize IPC handlers immediately
|
||||
setupIpcHandlers();
|
||||
|
||||
function createWindow() {
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
|
@ -84,6 +84,9 @@ contextBridge.exposeInMainWorld('electron', {
|
||||
ipcRenderer.removeListener(channel, onProgress);
|
||||
});
|
||||
},
|
||||
|
||||
meilisearchSearch: async (indexName: string, query: string): Promise<{ success: boolean; data: any[] }> =>
|
||||
ipcRenderer.invoke('meilisearch-search', indexName, query),
|
||||
|
||||
// Window Controls
|
||||
minimizeWindow: () => ipcRenderer.invoke('window-minimize'),
|
||||
|
@ -1,13 +1,89 @@
|
||||
import { MeiliSearch } from 'meilisearch'
|
||||
import { MeiliSearch } from 'meilisearch';
|
||||
import slugify from 'slugify';
|
||||
const { platform } = require('os');
|
||||
const path = require('path');
|
||||
const spawn = require('child_process').spawn;
|
||||
|
||||
class MeilisearchService {
|
||||
private client: MeiliSearch;
|
||||
private binaryPath: string;
|
||||
private serverProcess: any | null = null;
|
||||
|
||||
constructor() {
|
||||
this.client = new MeiliSearch({
|
||||
host: 'http://localhost:7700',
|
||||
});
|
||||
this.createIndex('files'); // Ensure the index exists
|
||||
this.binaryPath = this.getBinaryPath();
|
||||
}
|
||||
|
||||
private getBinaryPath(): string {
|
||||
const arch = process.arch;
|
||||
let binaryName: string;
|
||||
|
||||
switch (platform()) {
|
||||
case 'darwin':
|
||||
binaryName = arch === 'arm64' ? 'meilisearch-macos-arm' : 'meilisearch-macos-x64';
|
||||
break;
|
||||
case 'win32':
|
||||
binaryName = 'meilisearch-windows.exe';
|
||||
break;
|
||||
case 'linux':
|
||||
binaryName = arch === 'arm' ? 'meilisearch-linux-arm' : 'meilisearch-linux-x64';
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported platform: ${platform()}`);
|
||||
}
|
||||
|
||||
return path.join(__dirname, '..', 'meilisearch_binaries', binaryName);
|
||||
}
|
||||
|
||||
public async startServer(): Promise<void> {
|
||||
if (this.serverProcess) {
|
||||
console.log('Meilisearch server is already running.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const apiKey = 'Damie'; // Use the system's name as the API key
|
||||
process.env.MEILISEARCH_MASTER_KEY = apiKey;
|
||||
|
||||
this.serverProcess = spawn(this.binaryPath, ['--http-addr', '127.0.0.1:7700'], {
|
||||
env: process.env, // Pass the environment variables to the child process
|
||||
});
|
||||
|
||||
this.serverProcess.stdout?.on('data', (data) => {
|
||||
console.log(`Meilisearch: ${data}`);
|
||||
});
|
||||
|
||||
this.serverProcess.stderr?.on('data', (data) => {
|
||||
console.error(`Meilisearch: ${data}`);
|
||||
});
|
||||
|
||||
this.serverProcess.on('close', (code) => {
|
||||
console.log(`Meilisearch server stopped with code ${code}`);
|
||||
this.serverProcess = null;
|
||||
});
|
||||
|
||||
this.serverProcess.on('error', (err) => {
|
||||
console.error('Failed to start Meilisearch server:', err);
|
||||
this.serverProcess = null;
|
||||
});
|
||||
|
||||
console.log('Meilisearch server started.');
|
||||
} catch (error) {
|
||||
console.error('Failed to start Meilisearch server:', error);
|
||||
}
|
||||
}
|
||||
|
||||
public async stopServer(): Promise<void> {
|
||||
if (!this.serverProcess) {
|
||||
console.log('Meilisearch server is not running.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.serverProcess.kill();
|
||||
this.serverProcess = null;
|
||||
console.log('Meilisearch server stopped.');
|
||||
}
|
||||
|
||||
// Implement methods for adding, removing, and updating documents and collections
|
||||
@ -68,17 +144,26 @@ class MeilisearchService {
|
||||
public async getDocuments(indexName: string): Promise<any[]> {
|
||||
try {
|
||||
const index = this.client.index(indexName);
|
||||
const { hits } = await index.search(''); // Empty search to get all documents
|
||||
return hits;
|
||||
const documents = await index.getDocuments();
|
||||
return documents.results;
|
||||
} catch (error) {
|
||||
console.error(`Failed to get documents from index ${indexName}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
private slugifyPath(path):string {
|
||||
return path
|
||||
.toLowerCase() // Convert to lowercase
|
||||
.replace(/[\\\/:*?"<>|]/g, '-') // Replace invalid characters with a dash
|
||||
.replace(/\s+/g, '-') // Replace spaces with a dash
|
||||
.replace(/-+/g, '-') // Replace multiple dashes with a single dash
|
||||
.replace(/^-+|-+$/g, '');
|
||||
}
|
||||
|
||||
public async search(indexName: string, query: string): Promise<any[]> {
|
||||
try {
|
||||
const index = this.client.index(indexName);
|
||||
console.log(indexName)
|
||||
const index = this.client.index(this.slugifyPath(indexName));
|
||||
const { hits } = await index.search(query);
|
||||
return hits;
|
||||
} catch (error) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data:;" />
|
||||
<title>Data Identification Manager : Personal Edition</title>
|
||||
<title>Data Identification Manager</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
12
electron-file-search/package-lock.json
generated
12
electron-file-search/package-lock.json
generated
@ -26,7 +26,8 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-markdown": "^9.0.3",
|
||||
"react-virtuoso": "^4.12.5",
|
||||
"rehype-highlight": "^7.0.2"
|
||||
"rehype-highlight": "^7.0.2",
|
||||
"slugify": "^1.6.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.16",
|
||||
@ -8910,6 +8911,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/slugify": {
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
|
||||
"integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/smart-buffer": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||
|
@ -31,7 +31,8 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-markdown": "^9.0.3",
|
||||
"react-virtuoso": "^4.12.5",
|
||||
"rehype-highlight": "^7.0.2"
|
||||
"rehype-highlight": "^7.0.2",
|
||||
"slugify": "^1.6.6"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.electron-file-search",
|
||||
|
@ -154,7 +154,7 @@ function AppContent() {
|
||||
}}
|
||||
>
|
||||
|
||||
<Button
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
startIcon={<UpgradeTwoTone />}
|
||||
@ -250,7 +250,7 @@ function AppContent() {
|
||||
}}
|
||||
>
|
||||
Enterprise
|
||||
</Button>
|
||||
</Button>
|
||||
<Typography
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
@ -266,20 +266,7 @@ function AppContent() {
|
||||
Data Identification Manager
|
||||
</Typography>
|
||||
<div className="window-controls">
|
||||
<Chip
|
||||
label="Personal Edition (Free)"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
sx={{
|
||||
bgcolor: 'primary.main', // or 'secondary.main', 'error.main', etc.
|
||||
color: 'primary.contrastText',
|
||||
borderRadius: 1,
|
||||
height: 'auto',
|
||||
'& .MuiChip-label': {
|
||||
padding: '4px 8px',
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<button
|
||||
className="control-button"
|
||||
onClick={() => window.electron.closeWindow()}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Button,
|
||||
Modal,
|
||||
TextField,
|
||||
@ -29,54 +28,67 @@ interface FileData {
|
||||
}
|
||||
|
||||
const columns: GridColDef[] = [
|
||||
{ field: 'id', headerName: 'ID', width: 50 },
|
||||
{ field: 'filePath', headerName: 'File Path', width: 250 },
|
||||
{ field: 'fileName', headerName: 'File Name', width: 150 },
|
||||
{ field: 'fileExtension', headerName: 'File Extension', width: 100 },
|
||||
{
|
||||
field: 'size',
|
||||
headerName: 'Size',
|
||||
width: 100,
|
||||
valueFormatter: (value:any) => {
|
||||
console.log("params.value:", value); // Debugging lin
|
||||
// Ensure the value is a number
|
||||
const bytes = typeof value === 'number' ? value : 0;
|
||||
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
|
||||
const k = 1024;
|
||||
const dm = 2;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
||||
},
|
||||
},
|
||||
{ field: 'createdAt', headerName: 'Created At', width: 150 },
|
||||
{ field: 'modifiedAt', headerName: 'Modified At', width: 150 },
|
||||
{ field: 'accessedAt', headerName: 'Accessed At', width: 150 },
|
||||
{ field: 'size', headerName: 'Size', width: 70 },
|
||||
];
|
||||
|
||||
|
||||
|
||||
const DirectorySearchModal: React.FC<DirectorySearchModalProps> = ({ dir, open, onClose, modalId }) => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [rows, setRows] = useState<FileData[]>([]);
|
||||
|
||||
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchQuery(event.target.value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchFileData = async () => {
|
||||
window.electron.getDocuments().then((response) => {
|
||||
if (response.success && response.data) {
|
||||
const filesInDirectory = response.data.filter(doc => doc.path.startsWith(dir));
|
||||
const fileData: FileData[] = filesInDirectory.map((doc, index) => ({
|
||||
id: index + 1,
|
||||
filePath: doc.path,
|
||||
fileName: doc.path.split(/[/\\]/).pop() || '',
|
||||
fileExtension: doc.path.split('.').pop() || '',
|
||||
createdAt: doc.createdAt || '',
|
||||
modifiedAt: doc.modifiedAt || '',
|
||||
accessedAt: doc.accessedAt || '',
|
||||
size: doc.size || 0,
|
||||
}));
|
||||
setRows(fileData);
|
||||
}
|
||||
});
|
||||
};
|
||||
handleSearchChange({ target: { value: '*' } } as any);
|
||||
}, []);
|
||||
|
||||
fetchFileData();
|
||||
}, [dir]);
|
||||
|
||||
const filteredRows = rows.filter((row) => {
|
||||
return (
|
||||
row.filePath.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
row.fileName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
row.fileExtension.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
});
|
||||
const handleSearchChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const query = event.target.value;
|
||||
setSearchQuery(query);
|
||||
|
||||
window.electron.meilisearchSearch(dir, query)
|
||||
.then((response: { success: boolean; data: any[]; error?: string }) => {
|
||||
if (response.success && response.data) {
|
||||
const fileData: FileData[] = response.data.map((doc: any, index: number) => ({
|
||||
id: doc.name,
|
||||
filePath: doc.name,
|
||||
fileName: doc.fileName,
|
||||
fileExtension: doc.extension,
|
||||
size: doc.size, // Ensure size is not undefined
|
||||
createdAt: doc.createdAt,
|
||||
modifiedAt: doc.modifiedAt,
|
||||
accessedAt: doc.accessedAt,
|
||||
}));
|
||||
setRows(fileData);
|
||||
} else {
|
||||
console.error('Error searching Meilisearch:', response.error);
|
||||
setRows([]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -131,19 +143,47 @@ const DirectorySearchModal: React.FC<DirectorySearchModalProps> = ({ dir, open,
|
||||
</Box>
|
||||
<div style={{ height: 400, width: '100%' }}>
|
||||
<DataGrid
|
||||
rows={filteredRows}
|
||||
rows={rows}
|
||||
columns={columns}
|
||||
checkboxSelection
|
||||
rowCount={rows.length}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
color="primary"
|
||||
onClick={onClose}
|
||||
sx={{ mt: 2 }}
|
||||
sx={{ mt: 2, m:1 }}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="warning"
|
||||
onClick={onClose}
|
||||
disabled
|
||||
sx={{ mt: 2, m:1 }}
|
||||
>
|
||||
Move To
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="warning"
|
||||
onClick={onClose}
|
||||
disabled
|
||||
sx={{ mt: 2, m:1 }}
|
||||
>
|
||||
Copy To
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={onClose}
|
||||
disabled
|
||||
sx={{ mt: 2, m:1 }}
|
||||
>
|
||||
Clean From System
|
||||
</Button>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
|
2
electron-file-search/src/electron.d.ts
vendored
2
electron-file-search/src/electron.d.ts
vendored
@ -19,7 +19,7 @@ declare global {
|
||||
sources: DocumentMetadata[];
|
||||
}>;
|
||||
getLLMConfig: () => Promise<LLMConfig>;
|
||||
|
||||
meilisearchSearch: (indexName: string, query: string) => Promise<{ success: boolean; data: any[] }>;
|
||||
// Vector Store Operations
|
||||
getDocuments: () => Promise<{ success: boolean; data?: DocumentMetadata[]; error?: string }>;
|
||||
addDocument: (content: string, metadata: DocumentMetadata) => Promise<void>;
|
||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@ -6,7 +6,8 @@
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@mui/x-data-grid": "^7.26.0",
|
||||
"meilisearch": "^0.48.2"
|
||||
"meilisearch": "^0.48.2",
|
||||
"slugify": "^1.6.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
@ -537,6 +538,15 @@
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/slugify": {
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
|
||||
"integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stylis": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@mui/x-data-grid": "^7.26.0",
|
||||
"meilisearch": "^0.48.2"
|
||||
"meilisearch": "^0.48.2",
|
||||
"slugify": "^1.6.6"
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user