f
This commit is contained in:
parent
b724e61d7f
commit
7a24d3cb98
1
electron-file-search/data.ms/VERSION
Normal file
1
electron-file-search/data.ms/VERSION
Normal file
@ -0,0 +1 @@
|
|||||||
|
1.10.2
|
BIN
electron-file-search/data.ms/auth/data.mdb
Normal file
BIN
electron-file-search/data.ms/auth/data.mdb
Normal file
Binary file not shown.
BIN
electron-file-search/data.ms/auth/lock.mdb
Normal file
BIN
electron-file-search/data.ms/auth/lock.mdb
Normal file
Binary file not shown.
1
electron-file-search/data.ms/instance-uid
Normal file
1
electron-file-search/data.ms/instance-uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
cd5f7d5b-1d5c-40ec-bb49-885d5809f14f
|
BIN
electron-file-search/data.ms/tasks/data.mdb
Normal file
BIN
electron-file-search/data.ms/tasks/data.mdb
Normal file
Binary file not shown.
BIN
electron-file-search/data.ms/tasks/lock.mdb
Normal file
BIN
electron-file-search/data.ms/tasks/lock.mdb
Normal file
Binary file not shown.
@ -221,6 +221,62 @@ export function setupIpcHandlers() {
|
|||||||
return { success: false, error: err.message };
|
return { success: false, error: err.message };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Meilisearch Handlers
|
||||||
|
ipcMain.handle('meilisearch-add-documents', async (_: unknown, indexName: string, documents: any[]) => {
|
||||||
|
try {
|
||||||
|
await (global as any).meilisearchService.addDocuments(indexName, documents);
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
const err = error as Error;
|
||||||
|
console.error('Error adding documents to Meilisearch:', err);
|
||||||
|
return { success: false, error: err.message };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('meilisearch-remove-documents', async (_: unknown, indexName: string, documentIds: string[]) => {
|
||||||
|
try {
|
||||||
|
await (global as any).meilisearchService.removeDocuments(indexName, documentIds);
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
const err = error as Error;
|
||||||
|
console.error('Error removing documents from Meilisearch:', err);
|
||||||
|
return { success: false, error: err.message };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('meilisearch-update-documents', async (_: unknown, indexName: string, documents: any[]) => {
|
||||||
|
try {
|
||||||
|
await (global as any).meilisearchService.updateDocuments(indexName, documents);
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
const err = error as Error;
|
||||||
|
console.error('Error updating documents in Meilisearch:', err);
|
||||||
|
return { success: false, error: err.message };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('meilisearch-create-index', async (_: unknown, indexName: string) => {
|
||||||
|
try {
|
||||||
|
await (global as any).meilisearchService.createIndex(indexName);
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
const err = error as Error;
|
||||||
|
console.error('Error creating index in Meilisearch:', err);
|
||||||
|
return { success: false, error: err.message };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('meilisearch-delete-index', async (_: unknown, indexName: string) => {
|
||||||
|
try {
|
||||||
|
await (global as any).meilisearchService.deleteIndex(indexName);
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
const err = error as Error;
|
||||||
|
console.error('Error deleting index in Meilisearch:', err);
|
||||||
|
return { success: false, error: err.message };
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default setupIpcHandlers;
|
export default setupIpcHandlers;
|
||||||
|
@ -4,10 +4,180 @@ const os = require('os');
|
|||||||
const spawn = require('child_process').spawn;
|
const spawn = require('child_process').spawn;
|
||||||
const { store: electronStore } = require('./store');
|
const { store: electronStore } = require('./store');
|
||||||
const { setupIpcHandlers } = require('./ipc/handlers');
|
const { setupIpcHandlers } = require('./ipc/handlers');
|
||||||
|
const { MeiliSearch } = require('meilisearch');
|
||||||
|
|
||||||
|
const slugify = (str: string) => {
|
||||||
|
return str
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9]+/g, '-')
|
||||||
|
.replace(/^-|-$/g, '');
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
// Initialize IPC handlers immediately
|
||||||
setupIpcHandlers();
|
setupIpcHandlers();
|
||||||
|
|
||||||
|
const meilisearchService = new MeilisearchService();
|
||||||
|
(global as any).meilisearchService = meilisearchService;
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
const mainWindow = new BrowserWindow({
|
const mainWindow = new BrowserWindow({
|
||||||
width: 1200,
|
width: 1200,
|
||||||
@ -123,6 +293,7 @@ function createWindow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
|
meilisearchService.startServer();
|
||||||
createWindow();
|
createWindow();
|
||||||
|
|
||||||
app.on('activate', () => {
|
app.on('activate', () => {
|
||||||
@ -167,4 +338,23 @@ ipcMain.handle('open-external', (_, url) => {
|
|||||||
return require('electron').shell.openExternal(url);
|
return require('electron').shell.openExternal(url);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Launch the server
|
||||||
|
if (process.env.NODE_ENV !== 'development') {
|
||||||
|
const serverProcess = spawn('node', [path.join(__dirname, '../scripts/dev.cjs')], {
|
||||||
|
stdio: 'inherit',
|
||||||
|
});
|
||||||
|
|
||||||
|
serverProcess.on('close', (code) => {
|
||||||
|
console.log(`Server process exited with code ${code}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
serverProcess.on('error', (err) => {
|
||||||
|
console.error('Failed to start server process:', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app.on('will-quit', () => {
|
||||||
|
meilisearchService.stopServer();
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = app;
|
module.exports = app;
|
||||||
|
@ -26,6 +26,11 @@ contextBridge.exposeInMainWorld('electron', {
|
|||||||
addExcludedPath: async (path: string): Promise<void> =>
|
addExcludedPath: async (path: string): Promise<void> =>
|
||||||
ipcRenderer.invoke('add-excluded-path', path),
|
ipcRenderer.invoke('add-excluded-path', path),
|
||||||
|
|
||||||
|
createIndex: async (folderPath: string): Promise<{
|
||||||
|
answer: string;
|
||||||
|
sources: DocumentMetadata[];
|
||||||
|
}> => ipcRenderer.invoke('meilisearch-create-index', folderPath),
|
||||||
|
|
||||||
// LLM Operations
|
// LLM Operations
|
||||||
queryLLM: async (question: string): Promise<{
|
queryLLM: async (question: string): Promise<{
|
||||||
answer: string;
|
answer: string;
|
||||||
@ -84,6 +89,9 @@ contextBridge.exposeInMainWorld('electron', {
|
|||||||
minimizeWindow: () => ipcRenderer.invoke('window-minimize'),
|
minimizeWindow: () => ipcRenderer.invoke('window-minimize'),
|
||||||
maximizeWindow: () => ipcRenderer.invoke('window-maximize'),
|
maximizeWindow: () => ipcRenderer.invoke('window-maximize'),
|
||||||
closeWindow: () => ipcRenderer.invoke('window-close'),
|
closeWindow: () => ipcRenderer.invoke('window-close'),
|
||||||
|
send: (channel: string, data: any) => {
|
||||||
|
ipcRenderer.send(channel, data);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Export types for TypeScript
|
// Export types for TypeScript
|
||||||
|
@ -2,6 +2,11 @@ import { FSWatcher } from 'chokidar';
|
|||||||
import * as chokidar from 'chokidar';
|
import * as chokidar from 'chokidar';
|
||||||
import Store from 'electron-store';
|
import Store from 'electron-store';
|
||||||
import { ServiceError } from '../types';
|
import { ServiceError } from '../types';
|
||||||
|
import * as path from 'path';
|
||||||
|
const fs = require('fs');
|
||||||
|
const fsPromises = fs.promises;
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
import MeilisearchService from './meilisearchService';
|
||||||
|
|
||||||
const store = new Store<{
|
const store = new Store<{
|
||||||
watchedPaths: string[];
|
watchedPaths: string[];
|
||||||
@ -11,10 +16,13 @@ const store = new Store<{
|
|||||||
class FileSystemService {
|
class FileSystemService {
|
||||||
private watchers: Map<string, FSWatcher>;
|
private watchers: Map<string, FSWatcher>;
|
||||||
private excludedPaths: Set<string>;
|
private excludedPaths: Set<string>;
|
||||||
|
private meilisearchService: MeilisearchService;
|
||||||
|
private readonly indexName = 'files';
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.watchers = new Map();
|
this.watchers = new Map();
|
||||||
this.excludedPaths = new Set(store.get('excludedPaths', []));
|
this.excludedPaths = new Set(store.get('excludedPaths', []));
|
||||||
|
this.meilisearchService = new MeilisearchService();
|
||||||
|
|
||||||
// Add example paths
|
// Add example paths
|
||||||
const examplePaths = [
|
const examplePaths = [
|
||||||
@ -32,6 +40,49 @@ class FileSystemService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private slugify(filePath: string): string {
|
||||||
|
return filePath
|
||||||
|
.replace(/[\\/]/g, '-') // Replace path separators with dashes
|
||||||
|
.replace(/[^a-zA-Z0-9_-]+/g, '') // Remove non-alphanumeric characters
|
||||||
|
.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async calculateFileHash(filePath: string): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const hash = crypto.createHash('sha256');
|
||||||
|
const stream = fs.createReadStream(filePath);
|
||||||
|
|
||||||
|
if (!stream) {
|
||||||
|
reject(new Error(`Failed to create read stream for ${filePath}`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.on('data', (data: any) => {
|
||||||
|
try {
|
||||||
|
hash.update(data);
|
||||||
|
} catch (dataError) {
|
||||||
|
reject(new Error(`Failed to update hash with data for ${filePath}: ${dataError}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.on('end', () => {
|
||||||
|
try {
|
||||||
|
resolve(hash.digest('hex'));
|
||||||
|
} catch (digestError) {
|
||||||
|
reject(new Error(`Failed to digest hash for ${filePath}: ${digestError}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.on('error', (streamError: any) => {
|
||||||
|
reject(new Error(`Read stream error for ${filePath}: ${streamError}`));
|
||||||
|
});
|
||||||
|
} catch (creationError: any) {
|
||||||
|
reject(new Error(`Failed to create read stream or hash for ${filePath}: ${creationError}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async startWatching(dirPath: string): Promise<void> {
|
public async startWatching(dirPath: string): Promise<void> {
|
||||||
if (this.watchers.has(dirPath)) {
|
if (this.watchers.has(dirPath)) {
|
||||||
throw new ServiceError(`Already watching directory: ${dirPath}`);
|
throw new ServiceError(`Already watching directory: ${dirPath}`);
|
||||||
@ -39,7 +90,7 @@ class FileSystemService {
|
|||||||
|
|
||||||
const watcher = chokidar.watch(dirPath, {
|
const watcher = chokidar.watch(dirPath, {
|
||||||
ignored: [
|
ignored: [
|
||||||
/(^|[\/\\])\../, // Ignore dotfiles
|
/(^|[\\/])\../, // Ignore dotfiles
|
||||||
'**/node_modules/**',
|
'**/node_modules/**',
|
||||||
...Array.from(this.excludedPaths),
|
...Array.from(this.excludedPaths),
|
||||||
],
|
],
|
||||||
@ -47,9 +98,58 @@ class FileSystemService {
|
|||||||
ignoreInitial: false,
|
ignoreInitial: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
watcher.on('add', path => {
|
const indexName = this.slugify(dirPath);
|
||||||
console.log(`File ${path} has been added`);
|
|
||||||
// TODO: Process file
|
watcher.on('add', async (filePath) => {
|
||||||
|
console.log(`File ${filePath} has been added`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const stats = await fs.promises.stat(filePath);
|
||||||
|
const slug = this.slugify(filePath);
|
||||||
|
const fileContent = await fs.promises.readFile(filePath, 'utf-8');
|
||||||
|
const fileExtension = path.extname(filePath);
|
||||||
|
const fileName = path.basename(filePath);
|
||||||
|
|
||||||
|
const permissions = {
|
||||||
|
read: !!(stats.mode & fs.constants.S_IRUSR),
|
||||||
|
write: !!(stats.mode & fs.constants.S_IWUSR),
|
||||||
|
execute: !!(stats.mode & fs.constants.S_IXUSR),
|
||||||
|
};
|
||||||
|
|
||||||
|
const document = {
|
||||||
|
id: slug,
|
||||||
|
name: filePath,
|
||||||
|
fileName: fileName,
|
||||||
|
content: fileContent,
|
||||||
|
extension: fileExtension,
|
||||||
|
createdAt: stats.birthtime,
|
||||||
|
modifiedAt: stats.mtime,
|
||||||
|
accessedAt: stats.atime,
|
||||||
|
size: stats.size,
|
||||||
|
permissions: permissions,
|
||||||
|
};
|
||||||
|
|
||||||
|
let fileHash: string | undefined;
|
||||||
|
try {
|
||||||
|
fileHash = await this.calculateFileHash(filePath);
|
||||||
|
document['hash'] = fileHash;
|
||||||
|
} catch (hashError) {
|
||||||
|
console.error(`Failed to calculate file hash for ${filePath}:`, hashError);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.meilisearchService) {
|
||||||
|
try {
|
||||||
|
await this.meilisearchService.addDocuments(indexName, [document]);
|
||||||
|
console.log(`Added document to Meilisearch: ${slug}`);
|
||||||
|
} catch (meilisearchError) {
|
||||||
|
console.error(`Failed to add document to Meilisearch:`, meilisearchError);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('Meilisearch service not initialized.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to add document to Meilisearch:`, error);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watcher.on('change', path => {
|
watcher.on('change', path => {
|
||||||
|
91
electron-file-search/electron/services/meilisearchService.ts
Normal file
91
electron-file-search/electron/services/meilisearchService.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { MeiliSearch } from 'meilisearch'
|
||||||
|
|
||||||
|
class MeilisearchService {
|
||||||
|
private client: MeiliSearch;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.client = new MeiliSearch({
|
||||||
|
host: 'http://localhost:7700',
|
||||||
|
});
|
||||||
|
this.createIndex('files'); // Ensure the index exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement methods for adding, removing, and updating documents and collections
|
||||||
|
// using the Meilisearch API.
|
||||||
|
public async addDocuments(indexName: string, documents: any[]): Promise<void> {
|
||||||
|
try {
|
||||||
|
const index = this.client.index(indexName)
|
||||||
|
await index.addDocuments(documents)
|
||||||
|
console.log(`Added ${documents.length} documents to index ${indexName}`);
|
||||||
|
} 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 = this.client.index(indexName)
|
||||||
|
await index.deleteDocuments(documentIds)
|
||||||
|
console.log(`Removed documents with IDs ${documentIds.join(',')} from index ${indexName}`);
|
||||||
|
} 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 = this.client.index(indexName)
|
||||||
|
await index.updateDocuments(documents)
|
||||||
|
console.log(`Updated ${documents.length} documents in index ${indexName}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to update documents in index ${indexName}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteIndex(indexName: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.client.deleteIndex(indexName);
|
||||||
|
console.log(`Deleted index ${indexName}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to delete index ${indexName}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createIndex(indexName: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await this.client.createIndex(indexName);
|
||||||
|
console.log(`Created index ${indexName}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to create index ${indexName}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to get documents from index ${indexName}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async search(indexName: string, query: string): Promise<any[]> {
|
||||||
|
try {
|
||||||
|
const index = this.client.index(indexName);
|
||||||
|
const { hits } = await index.search(query);
|
||||||
|
return hits;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to search index ${indexName} with query ${query}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MeilisearchService;
|
@ -10,6 +10,9 @@ export interface DocumentMetadata {
|
|||||||
type: string;
|
type: string;
|
||||||
lastModified: number;
|
lastModified: number;
|
||||||
size: number;
|
size: number;
|
||||||
|
createdAt?: string;
|
||||||
|
modifiedAt?: string;
|
||||||
|
accessedAt?: string;
|
||||||
hasEmbeddings?: boolean;
|
hasEmbeddings?: boolean;
|
||||||
hasOcr?: boolean;
|
hasOcr?: boolean;
|
||||||
}
|
}
|
||||||
|
92
electron-file-search/package-lock.json
generated
92
electron-file-search/package-lock.json
generated
@ -14,15 +14,18 @@
|
|||||||
"@fontsource/roboto": "^5.0.8",
|
"@fontsource/roboto": "^5.0.8",
|
||||||
"@mui/icons-material": "^5.15.7",
|
"@mui/icons-material": "^5.15.7",
|
||||||
"@mui/material": "^5.15.7",
|
"@mui/material": "^5.15.7",
|
||||||
|
"@mui/x-data-grid": "^7.26.0",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"electron-store": "^8.1.0",
|
"electron-store": "^8.1.0",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
|
"meilisearch": "^0.48.2",
|
||||||
"ollama": "^0.5.12",
|
"ollama": "^0.5.12",
|
||||||
"openai": "^4.82.0",
|
"openai": "^4.82.0",
|
||||||
"openrouter-client": "^1.2.0",
|
"openrouter-client": "^1.2.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-markdown": "^9.0.3",
|
"react-markdown": "^9.0.3",
|
||||||
|
"react-virtuoso": "^4.12.5",
|
||||||
"rehype-highlight": "^7.0.2"
|
"rehype-highlight": "^7.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -1848,6 +1851,64 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@mui/x-data-grid": {
|
||||||
|
"version": "7.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.26.0.tgz",
|
||||||
|
"integrity": "sha512-9RNQeT2OL6jBOCE0MSUH11ol3fV5Zs9MkGxUIAGXcy/Fui0rZRNFO1yLmWDZU5yvskiNmUZJHWV/qXh++ZFarA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.25.7",
|
||||||
|
"@mui/utils": "^5.16.6 || ^6.0.0",
|
||||||
|
"@mui/x-internals": "7.26.0",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"reselect": "^5.1.1",
|
||||||
|
"use-sync-external-store": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/react": "^11.9.0",
|
||||||
|
"@emotion/styled": "^11.8.1",
|
||||||
|
"@mui/material": "^5.15.14 || ^6.0.0",
|
||||||
|
"@mui/system": "^5.15.14 || ^6.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@emotion/styled": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/x-internals": {
|
||||||
|
"version": "7.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.26.0.tgz",
|
||||||
|
"integrity": "sha512-VxTCYQcZ02d3190pdvys2TDg9pgbvewAVakEopiOgReKAUhLdRlgGJHcOA/eAuGLyK1YIo26A6Ow6ZKlSRLwMg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.25.7",
|
||||||
|
"@mui/utils": "^5.16.6 || ^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@ -6713,6 +6774,12 @@
|
|||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/meilisearch": {
|
||||||
|
"version": "0.48.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.48.2.tgz",
|
||||||
|
"integrity": "sha512-auDB6grs3f/+dVzzPiAOOFnrVN/L87/+SWZfy56TpimDinWjU4u6Jc2o6lgeLluotpbMOFY1WpTTbqtUdMduQw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/merge2": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
@ -8275,6 +8342,16 @@
|
|||||||
"react-dom": ">=16.6.0"
|
"react-dom": ">=16.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-virtuoso": {
|
||||||
|
"version": "4.12.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-4.12.5.tgz",
|
||||||
|
"integrity": "sha512-YeCbRRsC9CLf0buD0Rct7WsDbzf+yBU1wGbo05/XjbcN2nJuhgh040m3y3+6HVogTZxEqVm45ac9Fpae4/MxRQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16 || >=17 || >= 18 || >= 19",
|
||||||
|
"react-dom": ">=16 || >=17 || >= 18 || >=19"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read-cache": {
|
"node_modules/read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
@ -8431,6 +8508,12 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/reselect": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.22.10",
|
"version": "1.22.10",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||||
@ -9594,6 +9677,15 @@
|
|||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/utf8-byte-length": {
|
"node_modules/utf8-byte-length": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
|
||||||
|
@ -19,15 +19,18 @@
|
|||||||
"@fontsource/roboto": "^5.0.8",
|
"@fontsource/roboto": "^5.0.8",
|
||||||
"@mui/icons-material": "^5.15.7",
|
"@mui/icons-material": "^5.15.7",
|
||||||
"@mui/material": "^5.15.7",
|
"@mui/material": "^5.15.7",
|
||||||
|
"@mui/x-data-grid": "^7.26.0",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"electron-store": "^8.1.0",
|
"electron-store": "^8.1.0",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
|
"meilisearch": "^0.48.2",
|
||||||
"ollama": "^0.5.12",
|
"ollama": "^0.5.12",
|
||||||
"openai": "^4.82.0",
|
"openai": "^4.82.0",
|
||||||
"openrouter-client": "^1.2.0",
|
"openrouter-client": "^1.2.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-markdown": "^9.0.3",
|
"react-markdown": "^9.0.3",
|
||||||
|
"react-virtuoso": "^4.12.5",
|
||||||
"rehype-highlight": "^7.0.2"
|
"rehype-highlight": "^7.0.2"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
|
@ -328,10 +328,9 @@ function AppContent() {
|
|||||||
<Tabs value={currentTab} onChange={handleTabChange}>
|
<Tabs value={currentTab} onChange={handleTabChange}>
|
||||||
<Tab label="Home" />
|
<Tab label="Home" />
|
||||||
<Tab label="Chat" />
|
<Tab label="Chat" />
|
||||||
<Tab label="Settings" />
|
|
||||||
<Tab disabled label="Scanning" />
|
|
||||||
<Tab disabled label="Reports" />
|
<Tab disabled label="Reports" />
|
||||||
<Tab disabled label="Cleanup" />
|
<Tab disabled label="Cleanup" />
|
||||||
|
<Tab label="Settings" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
@ -349,13 +348,10 @@ function AppContent() {
|
|||||||
<ChatPanel messages={messages} />
|
<ChatPanel messages={messages} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value={currentTab} index={2}>
|
<TabPanel value={currentTab} index={2}>
|
||||||
<SettingsPanel />
|
<ReportingPanel />
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={currentTab} index={3}>
|
|
||||||
<ScanningPanel />
|
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value={currentTab} index={4}>
|
<TabPanel value={currentTab} index={4}>
|
||||||
<ReportingPanel />
|
<SettingsPanel />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
15
electron-file-search/src/components/DirectorySearchModal.css
Normal file
15
electron-file-search/src/components/DirectorySearchModal.css
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
.search-icon {
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
152
electron-file-search/src/components/DirectorySearchModal.tsx
Normal file
152
electron-file-search/src/components/DirectorySearchModal.tsx
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Typography,
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
TextField,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { DataGrid, GridColDef } from '@mui/x-data-grid';
|
||||||
|
import { Search } from '@mui/icons-material';
|
||||||
|
import './DirectorySearchModal.css';
|
||||||
|
|
||||||
|
interface DirectorySearchModalProps {
|
||||||
|
dir: string;
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
modalId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileData {
|
||||||
|
id: number;
|
||||||
|
filePath: string;
|
||||||
|
fileName: string;
|
||||||
|
fileExtension: string;
|
||||||
|
createdAt: string;
|
||||||
|
modifiedAt: string;
|
||||||
|
accessedAt: string;
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
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: '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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
id={modalId}
|
||||||
|
open={open}
|
||||||
|
onClose={onClose}
|
||||||
|
aria-labelledby={`modal-title-${dir}`}
|
||||||
|
aria-describedby={`modal-description-${dir}`}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
width: '80%',
|
||||||
|
maxWidth: 1200,
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
boxShadow: 24,
|
||||||
|
p: 4,
|
||||||
|
borderRadius: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
border: '1px solid white',
|
||||||
|
borderRadius: '4px',
|
||||||
|
paddingLeft: '8px',
|
||||||
|
marginBottom: '8px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Search className="search-icon" sx={{ mr: 1, color: 'white' }} />
|
||||||
|
<TextField
|
||||||
|
placeholder={dir}
|
||||||
|
variant="standard"
|
||||||
|
fullWidth
|
||||||
|
onChange={handleSearchChange}
|
||||||
|
InputProps={{
|
||||||
|
disableUnderline: true,
|
||||||
|
style: {
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
'& .MuiInputBase-input': {
|
||||||
|
color: 'white',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<div style={{ height: 400, width: '100%' }}>
|
||||||
|
<DataGrid
|
||||||
|
rows={filteredRows}
|
||||||
|
columns={columns}
|
||||||
|
checkboxSelection
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="error"
|
||||||
|
onClick={onClose}
|
||||||
|
sx={{ mt: 2 }}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DirectorySearchModal;
|
@ -0,0 +1,39 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { TextField, Button, Box } from '@mui/material';
|
||||||
|
|
||||||
|
interface DirectorySearchPanelProps {
|
||||||
|
folderNameSlugged: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DirectorySearchPanel = ({ folderNameSlugged }: DirectorySearchPanelProps) => {
|
||||||
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
|
|
||||||
|
const handleSearchQueryChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSearchQuery(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
// Implement Meilisearch search here
|
||||||
|
console.log(`Searching ${folderNameSlugged} for ${searchQuery}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ padding: '16px', display: 'flex', flexDirection: 'column', height: '100%' }}>
|
||||||
|
<Box sx={{ display: 'flex', gap: '8px', marginBottom: '16px' }}>
|
||||||
|
<TextField
|
||||||
|
label="Search"
|
||||||
|
variant="outlined"
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={handleSearchQueryChange}
|
||||||
|
sx={{ flex: 1 }}
|
||||||
|
/>
|
||||||
|
<Button variant="contained" color="primary" onClick={handleSearch}>
|
||||||
|
Search
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
{/* Add search results component here */}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DirectorySearchPanel;
|
@ -96,6 +96,8 @@ export default function DirectoryPicker({ onSelect }: DirectoryPickerProps) {
|
|||||||
|
|
||||||
const handleSelect = () => {
|
const handleSelect = () => {
|
||||||
onSelect(currentPath);
|
onSelect(currentPath);
|
||||||
|
// Send the meilisearch-create-index event
|
||||||
|
electron.createIndex(currentPath);
|
||||||
handleClose();
|
handleClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,24 +1,43 @@
|
|||||||
import React from 'react';
|
import React, { useState, useEffect } from "react";
|
||||||
import { Box, Typography, List, ListItem, ListItemText, ListItemIcon, IconButton, Tooltip } from '@mui/material';
|
import {
|
||||||
import { Folder as FolderIcon, Psychology as LLMIcon, ImageSearch as OCRIcon } from '@mui/icons-material';
|
Box,
|
||||||
import { DocumentMetadata } from '../../../electron/types';
|
List,
|
||||||
import DirectoryPicker from './DirectoryPicker';
|
ListItem,
|
||||||
import { useFileSystem } from '../../hooks/useFileSystem';
|
ListItemText,
|
||||||
|
ListItemIcon,
|
||||||
|
IconButton,
|
||||||
|
Tooltip,
|
||||||
|
} from "@mui/material";
|
||||||
|
import {
|
||||||
|
Folder as FolderIcon,
|
||||||
|
Psychology as LLMIcon,
|
||||||
|
ImageSearch as OCRIcon,
|
||||||
|
Search as SearchIcon,
|
||||||
|
} from "@mui/icons-material";
|
||||||
|
import { DocumentMetadata } from "../../../electron/types";
|
||||||
|
import DirectoryPicker from "./DirectoryPicker";
|
||||||
|
import { useFileSystem } from "../../hooks/useFileSystem";
|
||||||
|
import DirectorySearchModal from "../DirectorySearchModal";
|
||||||
|
|
||||||
interface FileMetadata {
|
interface FileMetadata {
|
||||||
[path: string]: DocumentMetadata;
|
[path: string]: DocumentMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Directory {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function FileExplorer() {
|
export default function FileExplorer() {
|
||||||
const { watchedDirectories, watchDirectory, unwatchDirectory } = useFileSystem();
|
const { watchedDirectories, watchDirectory, unwatchDirectory } = useFileSystem();
|
||||||
const [fileMetadata, setFileMetadata] = React.useState<FileMetadata>({});
|
const [fileMetadata, setFileMetadata] = useState<FileMetadata>({});
|
||||||
|
const [openModal, setOpenModal] = useState<{ [key: string]: boolean }>({});
|
||||||
|
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
// Get document metadata from electron store
|
|
||||||
window.electron.getDocuments().then((response) => {
|
window.electron.getDocuments().then((response) => {
|
||||||
if (response.success && response.data) {
|
if (response.success && response.data) {
|
||||||
const metadata: FileMetadata = {};
|
const metadata: FileMetadata = {};
|
||||||
response.data.forEach(doc => {
|
response.data.forEach((doc) => {
|
||||||
metadata[doc.path] = doc;
|
metadata[doc.path] = doc;
|
||||||
});
|
});
|
||||||
setFileMetadata(metadata);
|
setFileMetadata(metadata);
|
||||||
@ -36,33 +55,41 @@ export default function FileExplorer() {
|
|||||||
await unwatchDirectory(dirPath);
|
await unwatchDirectory(dirPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOpenModal = (dir: string) => {
|
||||||
|
setOpenModal((prev) => ({ ...prev, [dir]: true }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseModal = (dir: string) => {
|
||||||
|
setOpenModal((prev) => ({ ...prev, [dir]: false }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const directoryList: Directory[] = watchedDirectories.map((dir) => ({
|
||||||
|
name: dir.split(/[/\\]/).pop() || dir,
|
||||||
|
path: dir,
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
flexDirection: 'column',
|
display: "flex",
|
||||||
height: '100%',
|
flexDirection: "column",
|
||||||
overflow: 'hidden'
|
height: "100%",
|
||||||
}}>
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DirectoryPicker directories={directoryList} onSelect={handleDirectorySelect} />
|
||||||
|
|
||||||
<DirectoryPicker onSelect={handleDirectorySelect} />
|
<Box sx={{ flex: 1, overflow: "auto" }}>
|
||||||
|
|
||||||
<Box sx={{ flex: 1, overflow: 'auto' }}>
|
|
||||||
<List>
|
<List>
|
||||||
{watchedDirectories.map((dir) => (
|
{watchedDirectories.map((dir) => {
|
||||||
<ListItem
|
const modalId = `modal-${dir}`;
|
||||||
key={dir}
|
|
||||||
secondaryAction={
|
return (
|
||||||
<IconButton
|
<ListItem key={dir} sx={{ display: "flex", alignItems: "center" }}>
|
||||||
edge="end"
|
<ListItemIcon sx={{ minWidth: 28 }}>
|
||||||
size="small"
|
<FolderIcon fontSize="small" />
|
||||||
onClick={() => handleDirectoryRemove(dir)}
|
</ListItemIcon>
|
||||||
>
|
|
||||||
×
|
|
||||||
</IconButton>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ListItemIcon sx={{ display: 'flex', gap: 0.5 }}>
|
|
||||||
<FolderIcon />
|
|
||||||
{fileMetadata[dir]?.hasEmbeddings && (
|
{fileMetadata[dir]?.hasEmbeddings && (
|
||||||
<Tooltip title="LLM Embeddings Enabled">
|
<Tooltip title="LLM Embeddings Enabled">
|
||||||
<LLMIcon color="primary" fontSize="small" />
|
<LLMIcon color="primary" fontSize="small" />
|
||||||
@ -73,20 +100,35 @@ export default function FileExplorer() {
|
|||||||
<OCRIcon color="secondary" fontSize="small" />
|
<OCRIcon color="secondary" fontSize="small" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={dir.split(/[/\\]/).pop()}
|
primary={dir.split(/[/\\]/).pop()}
|
||||||
secondary={dir}
|
secondary={dir}
|
||||||
secondaryTypographyProps={{
|
secondaryTypographyProps={{
|
||||||
sx: {
|
sx: {
|
||||||
overflow: 'hidden',
|
overflow: "hidden",
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: "ellipsis",
|
||||||
whiteSpace: 'nowrap'
|
whiteSpace: "nowrap",
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
|
||||||
))}
|
{/* Search Icon to Open Modal */}
|
||||||
|
<Tooltip title="Search">
|
||||||
|
<IconButton color="primary" size="small" onClick={() => handleOpenModal(dir)}>
|
||||||
|
<SearchIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<DirectorySearchModal
|
||||||
|
dir={dir}
|
||||||
|
open={!!openModal[dir]}
|
||||||
|
onClose={() => handleCloseModal(dir)}
|
||||||
|
modalId={modalId}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</List>
|
</List>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
557
package-lock.json
generated
Normal file
557
package-lock.json
generated
Normal file
@ -0,0 +1,557 @@
|
|||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"dependencies": {
|
||||||
|
"@mui/x-data-grid": "^7.26.0",
|
||||||
|
"meilisearch": "^0.48.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/runtime": {
|
||||||
|
"version": "7.26.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz",
|
||||||
|
"integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"regenerator-runtime": "^0.14.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@emotion/cache": {
|
||||||
|
"version": "11.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
|
||||||
|
"integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/memoize": "^0.9.0",
|
||||||
|
"@emotion/sheet": "^1.4.0",
|
||||||
|
"@emotion/utils": "^1.4.2",
|
||||||
|
"@emotion/weak-memoize": "^0.4.0",
|
||||||
|
"stylis": "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@emotion/hash": {
|
||||||
|
"version": "0.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
|
||||||
|
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/@emotion/memoize": {
|
||||||
|
"version": "0.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
|
||||||
|
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/@emotion/serialize": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/hash": "^0.9.2",
|
||||||
|
"@emotion/memoize": "^0.9.0",
|
||||||
|
"@emotion/unitless": "^0.10.0",
|
||||||
|
"@emotion/utils": "^1.4.2",
|
||||||
|
"csstype": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@emotion/sheet": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/@emotion/unitless": {
|
||||||
|
"version": "0.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
|
||||||
|
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/@emotion/utils": {
|
||||||
|
"version": "1.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
|
||||||
|
"integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/@emotion/weak-memoize": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/@mui/core-downloads-tracker": {
|
||||||
|
"version": "6.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.3.tgz",
|
||||||
|
"integrity": "sha512-hlyOzo2ObarllAOeT1ZSAusADE5NZNencUeIvXrdQ1Na+FL1lcznhbxfV5He1KqGiuR8Az3xtCUcYKwMVGFdzg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/material": {
|
||||||
|
"version": "6.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.3.tgz",
|
||||||
|
"integrity": "sha512-ubtQjplbWneIEU8Y+4b2VA0CDBlyH5I3AmVFGmsLyDe/bf0ubxav5t11c8Afem6rkSFWPlZA2DilxmGka1xiKQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.26.0",
|
||||||
|
"@mui/core-downloads-tracker": "^6.4.3",
|
||||||
|
"@mui/system": "^6.4.3",
|
||||||
|
"@mui/types": "^7.2.21",
|
||||||
|
"@mui/utils": "^6.4.3",
|
||||||
|
"@popperjs/core": "^2.11.8",
|
||||||
|
"@types/react-transition-group": "^4.4.12",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"csstype": "^3.1.3",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"react-is": "^19.0.0",
|
||||||
|
"react-transition-group": "^4.4.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/react": "^11.5.0",
|
||||||
|
"@emotion/styled": "^11.3.0",
|
||||||
|
"@mui/material-pigment-css": "^6.4.3",
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@emotion/styled": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@mui/material-pigment-css": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/private-theming": {
|
||||||
|
"version": "6.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.3.tgz",
|
||||||
|
"integrity": "sha512-7x9HaNwDCeoERc4BoEWLieuzKzXu5ZrhRnEM6AUcRXUScQLvF1NFkTlP59+IJfTbEMgcGg1wWHApyoqcksrBpQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.26.0",
|
||||||
|
"@mui/utils": "^6.4.3",
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/styled-engine": {
|
||||||
|
"version": "6.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.3.tgz",
|
||||||
|
"integrity": "sha512-OC402VfK+ra2+f12Gef8maY7Y9n7B6CZcoQ9u7mIkh/7PKwW/xH81xwX+yW+Ak1zBT3HYcVjh2X82k5cKMFGoQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.26.0",
|
||||||
|
"@emotion/cache": "^11.13.5",
|
||||||
|
"@emotion/serialize": "^1.3.3",
|
||||||
|
"@emotion/sheet": "^1.4.0",
|
||||||
|
"csstype": "^3.1.3",
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/react": "^11.4.1",
|
||||||
|
"@emotion/styled": "^11.3.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@emotion/styled": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/system": {
|
||||||
|
"version": "6.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.3.tgz",
|
||||||
|
"integrity": "sha512-Q0iDwnH3+xoxQ0pqVbt8hFdzhq1g2XzzR4Y5pVcICTNtoCLJmpJS3vI4y/OIM1FHFmpfmiEC2IRIq7YcZ8nsmg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.26.0",
|
||||||
|
"@mui/private-theming": "^6.4.3",
|
||||||
|
"@mui/styled-engine": "^6.4.3",
|
||||||
|
"@mui/types": "^7.2.21",
|
||||||
|
"@mui/utils": "^6.4.3",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"csstype": "^3.1.3",
|
||||||
|
"prop-types": "^15.8.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/react": "^11.5.0",
|
||||||
|
"@emotion/styled": "^11.3.0",
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@emotion/styled": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/types": {
|
||||||
|
"version": "7.2.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.21.tgz",
|
||||||
|
"integrity": "sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/utils": {
|
||||||
|
"version": "6.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.3.tgz",
|
||||||
|
"integrity": "sha512-jxHRHh3BqVXE9ABxDm+Tc3wlBooYz/4XPa0+4AI+iF38rV1/+btJmSUgG4shDtSWVs/I97aDn5jBCt6SF2Uq2A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.26.0",
|
||||||
|
"@mui/types": "^7.2.21",
|
||||||
|
"@types/prop-types": "^15.7.14",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"react-is": "^19.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/x-data-grid": {
|
||||||
|
"version": "7.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.26.0.tgz",
|
||||||
|
"integrity": "sha512-9RNQeT2OL6jBOCE0MSUH11ol3fV5Zs9MkGxUIAGXcy/Fui0rZRNFO1yLmWDZU5yvskiNmUZJHWV/qXh++ZFarA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.25.7",
|
||||||
|
"@mui/utils": "^5.16.6 || ^6.0.0",
|
||||||
|
"@mui/x-internals": "7.26.0",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
|
"reselect": "^5.1.1",
|
||||||
|
"use-sync-external-store": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/react": "^11.9.0",
|
||||||
|
"@emotion/styled": "^11.8.1",
|
||||||
|
"@mui/material": "^5.15.14 || ^6.0.0",
|
||||||
|
"@mui/system": "^5.15.14 || ^6.0.0",
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@emotion/styled": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@mui/x-internals": {
|
||||||
|
"version": "7.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.26.0.tgz",
|
||||||
|
"integrity": "sha512-VxTCYQcZ02d3190pdvys2TDg9pgbvewAVakEopiOgReKAUhLdRlgGJHcOA/eAuGLyK1YIo26A6Ow6ZKlSRLwMg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.25.7",
|
||||||
|
"@mui/utils": "^5.16.6 || ^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/mui-org"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@popperjs/core": {
|
||||||
|
"version": "2.11.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||||
|
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/popperjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/prop-types": {
|
||||||
|
"version": "15.7.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
|
||||||
|
"integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/react": {
|
||||||
|
"version": "19.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz",
|
||||||
|
"integrity": "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"csstype": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/react-transition-group": {
|
||||||
|
"version": "4.4.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
|
||||||
|
"integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/clsx": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/csstype": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/dom-helpers": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.8.7",
|
||||||
|
"csstype": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/js-tokens": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/loose-envify": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"loose-envify": "cli.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/meilisearch": {
|
||||||
|
"version": "0.48.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.48.2.tgz",
|
||||||
|
"integrity": "sha512-auDB6grs3f/+dVzzPiAOOFnrVN/L87/+SWZfy56TpimDinWjU4u6Jc2o6lgeLluotpbMOFY1WpTTbqtUdMduQw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prop-types": {
|
||||||
|
"version": "15.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
|
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"react-is": "^16.13.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prop-types/node_modules/react-is": {
|
||||||
|
"version": "16.13.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/react": {
|
||||||
|
"version": "19.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
|
||||||
|
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-dom": {
|
||||||
|
"version": "19.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
||||||
|
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"scheduler": "^0.25.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-is": {
|
||||||
|
"version": "19.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz",
|
||||||
|
"integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/react-transition-group": {
|
||||||
|
"version": "4.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
|
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"peer": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.5.5",
|
||||||
|
"dom-helpers": "^5.0.1",
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"prop-types": "^15.6.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.6.0",
|
||||||
|
"react-dom": ">=16.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/regenerator-runtime": {
|
||||||
|
"version": "0.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||||
|
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/reselect": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/scheduler": {
|
||||||
|
"version": "0.25.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
|
||||||
|
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/stylis": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/use-sync-external-store": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
package.json
Normal file
6
package.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@mui/x-data-grid": "^7.26.0",
|
||||||
|
"meilisearch": "^0.48.2"
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user