diff --git a/src/hooks/file-explorer.ts b/src/hooks/file-explorer.ts index 1e554f7..dda0f0b 100644 --- a/src/hooks/file-explorer.ts +++ b/src/hooks/file-explorer.ts @@ -12,7 +12,7 @@ export const useFileExplorer = () => { const { mode, setLoading } = useSearch(); const [results, setResults] = useState([]); const [error, setError] = useState(null); - + const useRegex = true; // Refs to store version and DB instance across renders const versionKeyRef = useRef(null); const dbInstanceRef = useRef(null); @@ -45,7 +45,7 @@ export const useFileExplorer = () => { const metadata = await response.json(); return metadata.version; } - } catch {} + } catch { } return patch; }, [], @@ -106,39 +106,52 @@ export const useFileExplorer = () => { const db = dbInstanceRef.current; // Get file list from cache or network - let files = await db.getFileList(); - if (!files) { + let file = await db.getFileList_NoCache(); + if (!file) { const filelistUrl = `${RAW_HOST}/${patch}/cdragon/files.exported.txt`; const response = await fetch(filelistUrl, { signal: abortController.signal, }); if (!response.ok) throw new Error("Failed to fetch file list"); const text = await response.text(); - files = text.split("\n").filter((line) => line.trim() !== ""); - await db.saveFileList(files); + //console.log("test_117"+text.split("\n")); + //const text=text2 + //file=await text.split("\n").filter((line) => line.trim() !== ""); + //console.log("raw filelist"+text); + db.saveFileList(text); } // Build regex with local prefix if in local mode + const prefix = localPrefix(); - const escapedQuery = escapeRegex(trimmedQuery); + + const content = useRegex ? trimmedQuery : escapeRegex(trimmedQuery); let regex: RegExp; try { - regex = new RegExp(`^${prefix}.*(?:${escapedQuery}).*$`, "mi"); - } catch (e) { + regex = new RegExp(`^${prefix}.*?(?:${content}).*$`, "gim"); + //const fast_regexp =new RegExp(`$\\n${prefix}[^\\n]*?${content}[^\\n]*\\n`,"gim") + //regex=fast_regexp; + } + catch (e) { console.error("Invalid regex", e); setResults([]); return; } - - // Perform search (worker‑based) + console.log(regex); const matches = await db.searchFileList(regex); - const currentPatch = getPatch(); - setResults( - matches.map((filename) => ({ - filename, - href: `/${currentPatch}/${filename}`, - })), - ); + if (matches) { + setResults( + matches.map((filename) => ({ + filename, + href: `/${currentPatch}/${filename}`, + })), + ); + } + else { + setResults( + [] + ); + } } catch (err) { if (err instanceof Error && err.name === "AbortError") return; setError(err instanceof Error ? err : new Error(String(err))); diff --git a/src/lib/db.ts b/src/lib/db.ts index 02df725..45bb47c 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -1,13 +1,15 @@ // lib/filelist-db.ts -const DB_NAME = "filelists"; -const STORE_NAME = "filelists"; +const DB_NAME = "file"; +const STORE_NAME = "file"; const DB_VERSION = 1; export class FilelistDB { private static dbConnection: Promise | null = null; - + //private static filelist=null; + private static file; private version: string; + private worker: Worker | null = null; private workerPromiseMap = new Map< string, @@ -18,28 +20,53 @@ export class FilelistDB { >(); constructor(version: string) { + this.version = version; + //console.log(version); + //FilelistDB.filelist=null; + //this.getFileList(); } private static async getConnection(): Promise { if (FilelistDB.dbConnection) return FilelistDB.dbConnection; - FilelistDB.dbConnection = new Promise((resolve, reject) => { + FilelistDB.dbConnection = new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, DB_VERSION); request.onerror = () => reject(request.error); request.onsuccess = () => resolve(request.result); request.onupgradeneeded = (event) => { + const db = (event.target as IDBOpenDBRequest).result; if (!db.objectStoreNames.contains(STORE_NAME)) { db.createObjectStore(STORE_NAME, { keyPath: "version" }); } + }; }); return FilelistDB.dbConnection; } - async getFileList(): Promise { + + + async getFileList(): Promise { + if (FilelistDB.file) return FilelistDB.file; + const db = await FilelistDB.getConnection(); + FilelistDB.file = new Promise((resolve, reject) => { + const tx = db.transaction(STORE_NAME, "readonly"); + const store = tx.objectStore(STORE_NAME); + const request = store.get(this.version); + request.onerror = () => reject(request.error); + request.onsuccess = () => { + const result = request.result; + //console.log(result); + resolve(result?.file); + }; + }); + return FilelistDB.file + } + async getFileList_NoCache(): Promise { + const db = await FilelistDB.getConnection(); return new Promise((resolve, reject) => { const tx = db.transaction(STORE_NAME, "readonly"); @@ -48,20 +75,25 @@ export class FilelistDB { request.onerror = () => reject(request.error); request.onsuccess = () => { const result = request.result; - resolve(result?.files); + //console.log(result); + resolve(result?.file); }; }); + } - async saveFileList(files: string[]): Promise { + async saveFileList(file: string): Promise { + + FilelistDB.file = null; const db = await FilelistDB.getConnection(); return new Promise((resolve, reject) => { const tx = db.transaction(STORE_NAME, "readwrite"); const store = tx.objectStore(STORE_NAME); - const request = store.put({ version: this.version, files }); + const request = store.put({ version: this.version, file }); request.onerror = () => reject(request.error); request.onsuccess = () => resolve(); }); + } async deleteFileList(): Promise { @@ -75,37 +107,57 @@ export class FilelistDB { }); } + + + + + /** * Search the file list of this version using a regular expression. * The search runs in a Web Worker if supported. - * @param regex The RegExp to test against each filename + * @param query The RegExp to test against each filename * @returns Array of matching filenames */ - async searchFileList(regex: RegExp): Promise { - const files = await this.getFileList(); - if (!files) return []; - + async searchFileList(query): Promise { + const start = Date.now(); + //const start3 = Date.now(); + const file = await this.getFileList(); + console.log("files-fetch: " + (Date.now() - start)); + if (!file) return []; + console.log("File loaded in "+ (Date.now()-start) +"ms"); // Fallback to main thread if workers are not supported if (typeof Worker === "undefined") { - return files.filter((file) => regex.test(file)); + //console.log("regex: "+regex); + //const start = Date.now(); + const start2 = Date.now(); + let res = file.match(query); + console.log("query: " + (Date.now() - start2)); + //console.log(res); + //console.log(res);//files.filter((file) => regex.test(file)); + //console.log("main-thered-search: "+(Date.now()-start)); + //console.log("total-search: "+(Date.now()-start2)); + + return res; } + this.initWorker(); const requestId = Math.random().toString(36).substring(2) + Date.now(); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { this.workerPromiseMap.set(requestId, { resolve, reject }); // biome-ignore lint/style/noNonNullAssertion: debug this.worker!.postMessage({ type: "search", - files, - pattern: regex.source, - flags: regex.flags, + file, + query, requestId, }); - }); + }).then((data) =>{console.log("total-query-time "+(Date.now()-start));return data;}); + + } private initWorker() { @@ -113,11 +165,13 @@ export class FilelistDB { const workerCode = ` self.onmessage = (e) => { - const { type, files, pattern, flags, requestId } = e.data; + const { type, file, query, requestId } = e.data; if (type === 'search') { try { - const regex = new RegExp(pattern, flags); - const matches = files.filter(file => regex.test(file)); + + const start = Date.now(); + const matches =file.match(query); + console.log("worker-query: "+(Date.now()-start)); self.postMessage({ type: 'result', matches, requestId }); } catch (err) { self.postMessage({ type: 'error', error: err.message, requestId });