diff --git a/CHANGELOG.md b/CHANGELOG.md
index d79e86b..7ca1991 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
# Changelog
+## Changes 4/12/2025
+
+- **Fuse.js Integration for Indexed Real-Time Searching**
+ - **Added Fuse.js Library:** Included Fuse.js via a CDN `
+
diff --git a/js/fileListView.js b/js/fileListView.js
index 5db8f97..69c0963 100644
--- a/js/fileListView.js
+++ b/js/fileListView.js
@@ -33,11 +33,8 @@ window.viewMode = localStorage.getItem("viewMode") || "table"; // "table" or "ga
*/
function parseSizeToBytes(sizeStr) {
if (!sizeStr) return 0;
- // Remove any whitespace
let s = sizeStr.trim();
- // Extract the numerical part.
let value = parseFloat(s);
- // Determine if there is a unit. Convert the unit to uppercase for easier matching.
let upper = s.toUpperCase();
if (upper.includes("KB")) {
value *= 1024;
@@ -50,7 +47,7 @@ function parseSizeToBytes(sizeStr) {
}
/**
- * Format the total bytes as a human-readable string, choosing an appropriate unit.
+ * Format the total bytes as a human-readable string.
*/
function formatSize(totalBytes) {
if (totalBytes < 1024) {
@@ -66,18 +63,33 @@ function formatSize(totalBytes) {
/**
* Build the folder summary HTML using the filtered file list.
- * This function sums the file sizes in bytes correctly, then formats the total.
*/
function buildFolderSummary(filteredFiles) {
const totalFiles = filteredFiles.length;
const totalBytes = filteredFiles.reduce((sum, file) => {
- // file.size might be something like "456.9KB" or just "1024".
return sum + parseSizeToBytes(file.size);
}, 0);
const sizeStr = formatSize(totalBytes);
return `Total Files: ${totalFiles} | Total Size: ${sizeStr}`;
}
+/**
+ * --- Fuse.js Search Helper ---
+ * Uses Fuse.js to perform a fuzzy search on fileData.
+ * Searches over file name, uploader, and tag names.
+ */
+function searchFiles(searchTerm) {
+ if (!searchTerm) return fileData;
+ // Define search options – adjust threshold as needed.
+ const options = {
+ keys: ['name', 'uploader', 'tags.name'],
+ threshold: 0.3
+ };
+ const fuse = new Fuse(fileData, options);
+ // Fuse returns an array of results where each result has an "item" property.
+ return fuse.search(searchTerm).map(result => result.item);
+}
+
/**
* --- VIEW MODE TOGGLE BUTTON & Helpers ---
*/
@@ -134,7 +146,15 @@ export function loadFileList(folderParam) {
})
.then(data => {
fileListContainer.innerHTML = ""; // Clear loading message.
- if (data.files && data.files.length > 0) {
+ if (data.files && Object.keys(data.files).length > 0) {
+ // In case the returned "files" is an object instead of an array, transform it.
+ if (!Array.isArray(data.files)) {
+ data.files = Object.entries(data.files).map(([name, meta]) => {
+ meta.name = name;
+ return meta;
+ });
+ }
+ // Process each file – add computed properties.
data.files = data.files.map(file => {
file.fullName = (file.path || file.name).trim().toLowerCase();
file.editable = canEditFile(file.name);
@@ -146,7 +166,7 @@ export function loadFileList(folderParam) {
});
fileData = data.files;
- // Update the file list actions area without removing existing buttons.
+ // Update file summary.
const actionsContainer = document.getElementById("fileListActions");
if (actionsContainer) {
let summaryElem = document.getElementById("fileSummary");
@@ -164,7 +184,7 @@ export function loadFileList(folderParam) {
summaryElem.innerHTML = buildFolderSummary(fileData);
}
- // Render the view normally.
+ // Render view based on the view mode.
if (window.viewMode === "gallery") {
renderGalleryView(folder);
} else {
@@ -193,8 +213,7 @@ export function loadFileList(folderParam) {
}
/**
- * Update renderFileTable so that it writes its content into the provided container.
- * If no container is provided, it defaults to the element with id "fileList".
+ * Update renderFileTable so it writes its content into the provided container.
*/
export function renderFileTable(folder, container) {
const fileListContent = container || document.getElementById("fileList");
@@ -202,11 +221,9 @@ export function renderFileTable(folder, container) {
const itemsPerPageSetting = parseInt(localStorage.getItem("itemsPerPage") || "10", 10);
let currentPage = window.currentPage || 1;
- const filteredFiles = fileData.filter(file => {
- const nameMatch = file.name.toLowerCase().includes(searchTerm);
- const tagMatch = file.tags && file.tags.some(tag => tag.name.toLowerCase().includes(searchTerm));
- return nameMatch || tagMatch;
- });
+ // Use Fuse.js search via our helper function.
+ const filteredFiles = searchFiles(searchTerm);
+
const totalFiles = filteredFiles.length;
const totalPages = Math.ceil(totalFiles / itemsPerPageSetting);
if (currentPage > totalPages) {
@@ -257,7 +274,7 @@ export function renderFileTable(folder, container) {
createViewToggleButton();
- // Setup event listeners as before...
+ // Setup event listeners.
const newSearchInput = document.getElementById("searchInput");
if (newSearchInput) {
newSearchInput.addEventListener("input", debounce(function () {
@@ -317,10 +334,8 @@ export function renderFileTable(folder, container) {
export function renderGalleryView(folder, container) {
const fileListContent = container || document.getElementById("fileList");
const searchTerm = (window.currentSearchTerm || "").toLowerCase();
- const filteredFiles = fileData.filter(file => {
- return file.name.toLowerCase().includes(searchTerm) ||
- (file.tags && file.tags.some(tag => tag.name.toLowerCase().includes(searchTerm)));
- });
+ // Use Fuse.js search for gallery view as well.
+ const filteredFiles = searchFiles(searchTerm);
const folderPath = folder === "root"
? "uploads/"
: "uploads/" + folder.split("/").map(encodeURIComponent).join("/") + "/";
diff --git a/js/i18n.js b/js/i18n.js
index 85d4fb3..2c5910c 100644
--- a/js/i18n.js
+++ b/js/i18n.js
@@ -5,7 +5,7 @@ const translations = {
"no_files_selected": "No files selected.",
"confirm_delete_files": "Are you sure you want to delete {count} selected file(s)?",
"element_not_found": "Element with id \"{id}\" not found.",
- "search_placeholder": "Search files or tag...",
+ "search_placeholder": "Search files, tags, or uploader...",
"file_name": "File Name",
"date_modified": "Date Modified",
"upload_date": "Upload Date",