deepsite-filesearch / index.html
o2satz's picture
Add 1 files
91a3034 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Advanced File Finder Pro</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Inter', sans-serif;
}
.file-icon {
color: #6366f1;
}
.folder-icon {
color: #f59e0b;
}
.sort-active {
background-color: #e0e7ff;
}
.file-row:hover {
background-color: #f8fafc;
transform: translateY(-1px);
}
.sidebar-item:hover {
background-color: #f1f5f9;
}
.transition-all {
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.card-shadow {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
}
.modal-shadow {
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.glow {
animation: glow 2s infinite alternate;
}
@keyframes glow {
from {
box-shadow: 0 0 0px 0px rgba(99, 102, 241, 0.3);
}
to {
box-shadow: 0 0 8px 2px rgba(99, 102, 241, 0.3);
}
}
.highlight {
background: linear-gradient(90deg, rgba(224,231,255,0) 0%, rgba(224,231,255,0.5) 50%, rgba(224,231,255,0) 100%);
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
.modal-enter-active, .modal-leave-active {
transition: opacity 0.3s ease;
}
.modal-enter-from, .modal-leave-to {
opacity: 0;
}
.toast {
position: fixed;
bottom: 20px;
right: 20px;
background: #4f46e5;
color: white;
padding: 12px 24px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transform: translateY(100px);
opacity: 0;
transition: all 0.3s ease;
z-index: 1000;
}
.toast.show {
transform: translateY(0);
opacity: 1;
}
.context-menu {
position: absolute;
background: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
z-index: 100;
min-width: 200px;
overflow: hidden;
display: none;
}
.context-menu-item {
padding: 8px 16px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
}
.context-menu-item:hover {
background: #f1f5f9;
}
.breadcrumb-item {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
}
.breadcrumb-item:hover {
color: #4f46e5;
}
.breadcrumb-separator {
margin: 0 4px;
color: #94a3b8;
}
</style>
</head>
<body class="bg-slate-50">
<div id="app" class="flex h-screen">
<!-- Sidebar -->
<div class="w-64 bg-white border-r border-slate-200 flex flex-col">
<div class="p-5 border-b border-slate-200">
<div class="flex items-center space-x-3 mb-6">
<div class="w-9 h-9 rounded-lg bg-indigo-100 flex items-center justify-center">
<i class="fas fa-search text-indigo-500"></i>
</div>
<h1 class="text-xl font-semibold text-slate-800">Advanced Finder Pro</h1>
</div>
<div class="relative">
<input
type="text"
id="sidebarSearch"
placeholder="Search locations..."
class="w-full px-4 py-2.5 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent placeholder-slate-400"
>
<i class="fas fa-search absolute right-3 top-3 text-slate-400"></i>
</div>
</div>
<div class="flex-1 overflow-y-auto">
<div id="sidebarLocations" class="p-2">
<!-- Locations will be populated here -->
</div>
</div>
<div class="p-4 border-t border-slate-200 text-xs text-slate-500 bg-slate-50">
<div class="flex items-center mb-1">
<i class="fas fa-database mr-2"></i>
<span>Indexed: <span id="indexedCount">2847</span> items</span>
</div>
<div class="flex items-center">
<i class="fas fa-sync-alt mr-2"></i>
<span>Last indexed: <span id="lastIndexed">5 minutes ago</span></span>
</div>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Toolbar -->
<div class="bg-white border-b border-slate-200 p-4 flex items-center justify-between">
<div class="flex items-center space-x-2">
<button
id="backBtn"
class="px-3 py-2 bg-white border border-slate-200 rounded-lg hover:bg-slate-50 flex items-center transition-all focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
title="Go back"
>
<i class="fas fa-arrow-left mr-2"></i>
</button>
<button
id="refreshBtn"
class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 flex items-center transition-all focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
title="Refresh"
>
<i class="fas fa-sync-alt mr-2"></i> Refresh
</button>
<button
id="indexBtn"
class="px-4 py-2 bg-white border border-slate-200 rounded-lg hover:bg-slate-50 flex items-center transition-all focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
title="Reindex files"
>
<i class="fas fa-database mr-2"></i>
<span id="indexBtnText">Index Now</span>
</button>
<div class="relative" id="deleteBtnContainer" style="display: none;">
<button
id="deleteBtn"
class="px-4 py-2 bg-red-50 text-red-600 rounded-lg hover:bg-red-100 flex items-center transition-all focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2"
title="Delete selected files"
>
<i class="fas fa-trash mr-2"></i> Delete (<span id="selectedCount">0</span>)
</button>
</div>
</div>
<div class="flex items-center space-x-3">
<div class="relative">
<input
type="text"
id="searchQuery"
placeholder="Search files..."
class="px-4 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent w-64 placeholder-slate-400"
>
<i class="fas fa-search absolute right-3 top-3 text-slate-400"></i>
</div>
<button
id="filtersBtn"
class="px-4 py-2 bg-white border border-slate-200 rounded-lg hover:bg-slate-50 flex items-center transition-all focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
title="Show filters"
>
<i class="fas fa-sliders-h mr-2"></i> Filters
</button>
<button
id="newBtn"
class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 flex items-center transition-all focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
title="Create new"
>
<i class="fas fa-plus mr-2"></i> New
</button>
</div>
</div>
<!-- Breadcrumbs -->
<div id="breadcrumbs" class="bg-white border-b border-slate-200 px-5 py-2 flex items-center text-sm">
<!-- Breadcrumbs will be populated here -->
</div>
<!-- Advanced Search Panel -->
<div
id="advancedSearchPanel"
class="bg-white border-b border-slate-200 px-5 py-4 transition-all duration-300 ease-in-out overflow-hidden"
style="max-height: 0;"
>
<div class="grid grid-cols-1 md:grid-cols-3 gap-5">
<div>
<label class="block text-sm font-medium text-slate-700 mb-2">File Type</label>
<select
id="fileTypeFilter"
class="w-full px-4 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
>
<option value="all">All Files</option>
<option value="documents">Documents</option>
<option value="images">Images</option>
<option value="audio">Audio</option>
<option value="video">Video</option>
<option value="archives">Archives</option>
<option value="folders">Folders</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-2">Author/Creator</label>
<input
type="text"
id="authorFilter"
placeholder="Search by author..."
class="w-full px-4 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent placeholder-slate-400"
>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-2">Size Range</label>
<div class="flex items-center space-x-3">
<input
type="number"
id="sizeMinFilter"
placeholder="Min (KB)"
class="w-full px-4 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent placeholder-slate-400"
>
<span class="text-sm text-slate-400">to</span>
<input
type="number"
id="sizeMaxFilter"
placeholder="Max (KB)"
class="w-full px-4 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent placeholder-slate-400"
>
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-2">Date Modified</label>
<select
id="dateRangeFilter"
class="w-full px-4 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
>
<option value="all">Any Time</option>
<option value="today">Today</option>
<option value="week">This Week</option>
<option value="month">This Month</option>
<option value="year">This Year</option>
<option value="custom">Custom Range</option>
</select>
</div>
<div id="customDateRangeContainer" style="display: none;" class="md:col-span-3">
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
<div>
<label class="block text-sm font-medium text-slate-700 mb-2">From</label>
<input
type="date"
id="dateFromFilter"
class="w-full px-4 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-2">To</label>
<input
type="date"
id="dateToFilter"
class="w-full px-4 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
>
</div>
</div>
</div>
</div>
</div>
<!-- File Table Header -->
<div class="bg-white border-b border-slate-200 px-5 py-3">
<div class="grid grid-cols-12 gap-4 text-xs font-medium text-slate-500 uppercase tracking-wider">
<div class="col-span-5 flex items-center">
<input
type="checkbox"
id="selectAllCheckbox"
class="mr-3 h-4 w-4 text-indigo-600 rounded border-slate-300 focus:ring-indigo-500"
>
<span
id="sortByName"
class="cursor-pointer flex items-center hover:text-slate-700 transition-colors"
>
Name
<i id="nameSortIcon" class="fas ml-1 fa-sort"></i>
</span>
</div>
<div
id="sortByAuthor"
class="col-span-2 cursor-pointer flex items-center hover:text-slate-700 transition-colors"
>
<span>Author</span>
<i id="authorSortIcon" class="fas ml-1 fa-sort"></i>
</div>
<div
id="sortByType"
class="col-span-2 cursor-pointer flex items-center hover:text-slate-700 transition-colors"
>
<span>Type</span>
<i id="typeSortIcon" class="fas ml-1 fa-sort"></i>
</div>
<div
id="sortBySize"
class="col-span-2 cursor-pointer flex items-center justify-end hover:text-slate-700 transition-colors"
>
<span>Size</span>
<i id="sizeSortIcon" class="fas ml-1 fa-sort"></i>
</div>
<div
id="sortByModified"
class="col-span-1 cursor-pointer flex items-center justify-end hover:text-slate-700 transition-colors"
>
<span>Modified</span>
<i id="modifiedSortIcon" class="fas ml-1 fa-sort"></i>
</div>
</div>
</div>
<!-- File List -->
<div id="fileList" class="flex-1 overflow-y-auto bg-white">
<div class="flex flex-col items-center justify-center h-64 text-slate-400">
<i class="fas fa-folder-open text-5xl mb-4 opacity-30"></i>
<p class="text-lg font-medium text-slate-500">Loading files...</p>
</div>
</div>
<!-- Status Bar -->
<div class="bg-slate-50 border-t border-slate-200 px-5 py-2.5 text-xs text-slate-500 flex justify-between">
<div>
<span id="statusCount">0 items</span>
</div>
<div class="flex items-center space-x-5">
<div class="flex items-center">
<span class="mr-2">Index status:</span>
<span id="indexStatus" class="text-slate-400">Idle</span>
</div>
<div class="flex items-center">
<i id="performanceIcon" class="fas fa-circle text-xs mr-1.5 text-emerald-500"></i>
<span id="performanceText">fast performance</span>
</div>
</div>
</div>
</div>
<!-- Context Menu -->
<div id="contextMenu" class="context-menu">
<div class="context-menu-item" id="contextOpen">
<i class="fas fa-folder-open"></i> Open
</div>
<div class="context-menu-item" id="contextDownload">
<i class="fas fa-download"></i> Download
</div>
<div class="context-menu-item" id="contextRename">
<i class="fas fa-pen"></i> Rename
</div>
<div class="context-menu-item" id="contextFavorite">
<i class="fas fa-star"></i> Add to Favorites
</div>
<div class="border-t border-slate-200"></div>
<div class="context-menu-item" id="contextDelete">
<i class="fas fa-trash text-red-500"></i> <span class="text-red-500">Delete</span>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div id="deleteModal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50 backdrop-blur-sm">
<div class="bg-white rounded-xl modal-shadow p-6 w-96">
<div class="flex items-start mb-5">
<div class="flex-shrink-0 mt-1">
<div class="w-10 h-10 rounded-full bg-red-100 flex items-center justify-center">
<i class="fas fa-exclamation-triangle text-red-500"></i>
</div>
</div>
<div class="ml-4">
<h3 class="text-lg font-medium text-slate-800">Delete Files</h3>
<p class="mt-1 text-sm text-slate-500">
Are you sure you want to delete <span id="deleteCount">0</span> file(s)? This action cannot be undone.
</p>
</div>
</div>
<div class="mt-6 flex justify-end space-x-3">
<button
id="cancelDeleteBtn"
type="button"
class="px-4 py-2 bg-white border border-slate-200 rounded-lg text-slate-700 hover:bg-slate-50 transition-colors focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
Cancel
</button>
<button
id="confirmDeleteBtn"
type="button"
class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2"
>
Delete
</button>
</div>
</div>
</div>
<!-- New File/Folder Modal -->
<div id="newModal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50 backdrop-blur-sm">
<div class="bg-white rounded-xl modal-shadow p-6 w-96">
<div class="flex items-start mb-5">
<div class="flex-shrink-0 mt-1">
<div class="w-10 h-10 rounded-full bg-indigo-100 flex items-center justify-center">
<i class="fas fa-plus text-indigo-500"></i>
</div>
</div>
<div class="ml-4">
<h3 class="text-lg font-medium text-slate-800">Create New</h3>
<p class="mt-1 text-sm text-slate-500">
Create a new file or folder in the current location.
</p>
</div>
</div>
<div class="mt-4">
<div class="flex items-center space-x-2 mb-4">
<button id="newFileBtn" class="flex-1 px-4 py-3 border border-slate-200 rounded-lg hover:bg-slate-50 flex flex-col items-center">
<i class="fas fa-file text-indigo-500 text-xl mb-2"></i>
<span>File</span>
</button>
<button id="newFolderBtn" class="flex-1 px-4 py-3 border border-slate-200 rounded-lg hover:bg-slate-50 flex flex-col items-center">
<i class="fas fa-folder text-amber-500 text-xl mb-2"></i>
<span>Folder</span>
</button>
</div>
<div id="newFormContainer" style="display: none;">
<div class="mb-4">
<label class="block text-sm font-medium text-slate-700 mb-1">Name</label>
<input
type="text"
id="newNameInput"
placeholder="Enter name..."
class="w-full px-4 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent placeholder-slate-400"
>
</div>
<div class="flex justify-end space-x-3">
<button
id="cancelNewBtn"
type="button"
class="px-4 py-2 bg-white border border-slate-200 rounded-lg text-slate-700 hover:bg-slate-50 transition-colors focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
Cancel
</button>
<button
id="confirmNewBtn"
type="button"
class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
>
Create
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Toast Notification -->
<div id="toast" class="toast">
<div class="flex items-center">
<i id="toastIcon" class="fas fa-check-circle mr-2"></i>
<span id="toastMessage">Operation completed successfully</span>
</div>
</div>
</div>
<script>
// Enhanced application state
const state = {
sidebarSearch: '',
currentLocation: { id: 1, name: 'Home', icon: 'fas fa-home', path: [] },
indexedCount: 2847,
lastIndexed: '5 minutes ago',
searchQuery: '',
showAdvancedSearch: false,
isIndexing: false,
activeSort: 'name',
sortDirection: 'asc',
selectedFiles: [],
showDeleteConfirm: false,
contextMenuFile: null,
filters: {
fileType: 'all',
author: '',
sizeMin: '',
sizeMax: '',
dateRange: 'all',
dateFrom: '',
dateTo: ''
},
performanceRating: 'fast',
newItemType: 'file',
locations: [
{ id: 1, name: 'Home', icon: 'fas fa-home', path: [] },
{ id: 2, name: 'Documents', icon: 'fas fa-folder', path: ['Home'] },
{ id: 3, name: 'Downloads', icon: 'fas fa-download', path: ['Home'] },
{ id: 4, name: 'Pictures', icon: 'fas fa-image', path: ['Home'] },
{ id: 5, name: 'Music', icon: 'fas fa-music', path: ['Home'] },
{ id: 6, name: 'Videos', icon: 'fas fa-video', path: ['Home'] },
{ id: 7, name: 'Desktop', icon: 'fas fa-desktop', path: ['Home'] }
],
files: [
{ id: 1, name: 'Project Proposal.pdf', path: ['Home', 'Documents', 'Projects'], author: 'John Doe', type: 'PDF Document', size: 2456, modified: '2023-06-15T14:30:00', isFavorite: false },
{ id: 2, name: 'Budget 2023.xlsx', path: ['Home', 'Documents', 'Finance'], author: 'Jane Smith', type: 'Excel Spreadsheet', size: 4532, modified: '2023-05-22T09:45:00', isFavorite: true },
{ id: 3, name: 'Team Photo.jpg', path: ['Home', 'Pictures', 'Work'], author: 'Mike Johnson', type: 'JPEG Image', size: 3245, modified: '2023-06-10T11:20:00', isFavorite: false },
{ id: 4, name: 'Meeting Notes.docx', path: ['Home', 'Documents', 'Meetings'], author: 'Sarah Wilson', type: 'Word Document', size: 876, modified: '2023-06-14T16:10:00', isFavorite: false },
{ id: 5, name: 'Presentation.pptx', path: ['Home', 'Documents', 'Presentations'], author: 'David Brown', type: 'PowerPoint', size: 6543, modified: '2023-06-12T13:30:00', isFavorite: false },
{ id: 6, name: 'Project Backup.zip', path: ['Home', 'Downloads'], author: 'Emma Davis', type: 'ZIP Archive', size: 24567, modified: '2023-06-09T18:05:00', isFavorite: false },
{ id: 7, name: 'Interview.mp3', path: ['Home', 'Music', 'Recordings'], author: 'James Miller', type: 'MP3 Audio', size: 12345, modified: '2023-05-30T10:15:00', isFavorite: false },
{ id: 8, name: 'Tutorial.mp4', path: ['Home', 'Videos', 'Learning'], author: 'Olivia Wilson', type: 'MP4 Video', size: 45678, modified: '2023-05-28T14:20:00', isFavorite: false },
{ id: 9, name: 'Configuration.ini', path: ['Home', 'System'], author: 'System', type: 'Configuration File', size: 23, modified: '2023-06-13T12:40:00', isFavorite: false },
{ id: 10, name: 'Code Repository', path: ['Home', 'Documents', 'Projects'], author: 'John Doe', type: 'Folder', size: 10245, modified: '2023-06-05T08:30:00', isFavorite: true }
],
locationHistory: [],
historyIndex: -1
};
// Helper functions
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
if (typeof bytes === 'string') bytes = parseInt(bytes);
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {month: 'short', day: 'numeric'});
}
function formatTime(dateString) {
const date = new Date(dateString);
return date.toLocaleTimeString('en-US', {hour: '2-digit', minute: '2-digit'});
}
function getFileIcon(file) {
if (file.type === 'Folder') return 'fas fa-folder folder-icon';
const extension = file.name.split('.').pop().toLowerCase();
const iconMap = {
'pdf': 'fas fa-file-pdf file-icon',
'docx': 'fas fa-file-word file-icon',
'xlsx': 'fas fa-file-excel file-icon',
'pptx': 'fas fa-file-powerpoint file-icon',
'jpg': 'fas fa-file-image file-icon',
'jpeg': 'fas fa-file-image file-icon',
'png': 'fas fa-file-image file-icon',
'gif': 'fas fa-file-image file-icon',
'mp3': 'fas fa-file-audio file-icon',
'wav': 'fas fa-file-audio file-icon',
'mp4': 'fas fa-file-video file-icon',
'mov': 'fas fa-file-video file-icon',
'zip': 'fas fa-file-archive file-icon',
'rar': 'fas fa-file-archive file-icon',
'ini': 'fas fa-file-code file-icon'
};
return iconMap[extension] || 'fas fa-file file-icon';
}
function showToast(message, type = 'success') {
const toast = document.getElementById('toast');
const toastIcon = document.getElementById('toastIcon');
const toastMessage = document.getElementById('toastMessage');
// Set icon and color based on type
if (type === 'success') {
toastIcon.className = 'fas fa-check-circle mr-2';
toast.style.backgroundColor = '#4f46e5';
} else if (type === 'error') {
toastIcon.className = 'fas fa-exclamation-circle mr-2';
toast.style.backgroundColor = '#ef4444';
} else if (type === 'warning') {
toastIcon.className = 'fas fa-exclamation-triangle mr-2';
toast.style.backgroundColor = '#f59e0b';
} else if (type === 'info') {
toastIcon.className = 'fas fa-info-circle mr-2';
toast.style.backgroundColor = '#3b82f6';
}
toastMessage.textContent = message;
toast.classList.add('show');
// Hide after 3 seconds
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
function filterFiles() {
let files = [...state.files];
const filters = state.filters;
const search = state.searchQuery.toLowerCase();
// Filter by current location
files = files.filter(file =>
JSON.stringify(file.path) === JSON.stringify(state.currentLocation.path)
);
if (search) {
files = files.filter(file =>
file.name.toLowerCase().includes(search) ||
file.type.toLowerCase().includes(search) ||
(file.author && file.author.toLowerCase().includes(search))
);
}
if (filters.fileType !== 'all') {
const typeMap = {
'documents': ['PDF Document', 'Word Document', 'Excel Spreadsheet', 'PowerPoint'],
'images': ['JPEG Image', 'PNG Image', 'GIF Image'],
'audio': ['MP3 Audio', 'WAV Audio'],
'video': ['MP4 Video', 'MOV Video'],
'archives': ['ZIP Archive', 'RAR Archive'],
'folders': ['Folder']
};
const types = typeMap[filters.fileType] || [];
files = files.filter(file =>
types.includes(file.type) ||
(filters.fileType === 'documents' && file.type.includes('Document'))
);
}
if (filters.author) {
const authorSearch = filters.author.toLowerCase();
files = files.filter(file =>
file.author && file.author.toLowerCase().includes(authorSearch)
);
}
if (filters.sizeMin || filters.sizeMax) {
const min = filters.sizeMin ? parseInt(filters.sizeMin) : 0;
const max = filters.sizeMax ? parseInt(filters.sizeMax) : Infinity;
files = files.filter(file => file.size >= min && file.size <= max);
}
if (filters.dateRange && filters.dateRange !== 'all') {
const now = new Date();
let fromDate = new Date();
switch (filters.dateRange) {
case 'today':
fromDate.setHours(0, 0, 0, 0);
break;
case 'week':
fromDate.setDate(fromDate.getDate() - 7);
break;
case 'month':
fromDate.setMonth(fromDate.getMonth() - 1);
break;
case 'year':
fromDate.setFullYear(fromDate.getFullYear() - 1);
break;
case 'custom':
if (filters.dateFrom) {
fromDate = new Date(filters.dateFrom);
}
break;
}
files = files.filter(file => {
const fileDate = new Date(file.modified);
return fileDate >= fromDate;
});
if (filters.dateRange === 'custom' && filters.dateTo) {
const toDate = new Date(filters.dateTo);
toDate.setHours(23, 59, 59, 999);
files = files.filter(file => {
const fileDate = new Date(file.modified);
return fileDate <= toDate;
});
}
}
return sortFiles(files);
}
function sortFiles(files) {
const activeSort = state.activeSort;
const sortDirection = state.sortDirection;
return files.sort((a, b) => {
let aVal, bVal;
switch (activeSort) {
case 'name':
aVal = a.name.toLowerCase();
bVal = b.name.toLowerCase();
break;
case 'author':
aVal = a.author ? a.author.toLowerCase() : '';
bVal = b.author ? b.author.toLowerCase() : '';
break;
case 'type':
aVal = a.type.toLowerCase();
bVal = b.type.toLowerCase();
break;
case 'size':
aVal = a.size;
bVal = b.size;
break;
case 'modified':
aVal = new Date(a.modified);
bVal = new Date(b.modified);
break;
default:
aVal = a.name.toLowerCase();
bVal = b.name.toLowerCase();
}
if (aVal < bVal) return sortDirection === 'asc' ? -1 : 1;
if (aVal > bVal) return sortDirection === 'asc' ? 1 : -1;
return 0;
});
}
function filterLocations() {
const search = state.sidebarSearch.toLowerCase();
return state.locations.filter(loc =>
loc.name.toLowerCase().includes(search)
);
}
function updateBreadcrumbs() {
const breadcrumbs = document.getElementById('breadcrumbs');
breadcrumbs.innerHTML = '';
// Add home breadcrumb
const homeCrumb = document.createElement('div');
homeCrumb.className = 'breadcrumb-item';
homeCrumb.innerHTML = `
<i class="fas fa-home"></i>
<span>Home</span>
`;
homeCrumb.addEventListener('click', () => {
const homeLocation = state.locations.find(loc => loc.id === 1);
selectLocation(homeLocation);
});
breadcrumbs.appendChild(homeCrumb);
// Add path breadcrumbs
state.currentLocation.path.forEach((pathItem, index) => {
const separator = document.createElement('div');
separator.className = 'breadcrumb-separator';
separator.innerHTML = '<i class="fas fa-chevron-right"></i>';
breadcrumbs.appendChild(separator);
const crumb = document.createElement('div');
crumb.className = 'breadcrumb-item';
crumb.textContent = pathItem;
crumb.addEventListener('click', () => {
// Find the location that matches this path
const targetPath = state.currentLocation.path.slice(0, index + 1);
const targetLocation = state.locations.find(loc =>
loc.name === pathItem && JSON.stringify(loc.path) === JSON.stringify(targetPath.slice(0, -1))
);
if (targetLocation) {
selectLocation(targetLocation);
}
});
breadcrumbs.appendChild(crumb);
});
// Add current location if it's not home
if (state.currentLocation.id !== 1) {
const separator = document.createElement('div');
separator.className = 'breadcrumb-separator';
separator.innerHTML = '<i class="fas fa-chevron-right"></i>';
breadcrumbs.appendChild(separator);
const currentCrumb = document.createElement('div');
currentCrumb.className = 'breadcrumb-item font-medium text-indigo-600';
currentCrumb.textContent = state.currentLocation.name;
breadcrumbs.appendChild(currentCrumb);
}
}
// UI update functions
function updateSidebar() {
const sidebarLocations = document.getElementById('sidebarLocations');
const filteredLocations = filterLocations();
sidebarLocations.innerHTML = '';
filteredLocations.forEach(location => {
const locationEl = document.createElement('div');
locationEl.className = `sidebar-item px-3 py-2.5 rounded-lg cursor-pointer flex items-center transition-all group ${
state.currentLocation.id === location.id ? 'bg-indigo-50' : ''
}`;
locationEl.innerHTML = `
<div class="w-8 h-8 rounded-md flex items-center justify-center mr-2 ${
state.currentLocation.id === location.id ? 'bg-indigo-100' : 'bg-slate-100 group-hover:bg-slate-200'
}">
<i class="${location.icon} text-slate-600 group-hover:text-slate-800 ${
state.currentLocation.id === location.id ? 'text-indigo-500' : ''
}"></i>
</div>
<span class="text-sm font-medium truncate ${
state.currentLocation.id === location.id ? 'text-indigo-600' : 'text-slate-700 group-hover:text-slate-900'
}">
${location.name}
</span>
`;
locationEl.addEventListener('click', () => selectLocation(location));
sidebarLocations.appendChild(locationEl);
});
}
function updateFileList() {
const fileList = document.getElementById('fileList');
const filteredFiles = filterFiles();
if (filteredFiles.length === 0) {
fileList.innerHTML = `
<div class="flex flex-col items-center justify-center h-64 text-slate-400">
<i class="fas fa-folder-open text-5xl mb-4 opacity-30"></i>
<p class="text-lg font-medium text-slate-500">No files found</p>
<p class="text-sm mt-1 text-slate-400">Try adjusting your search or filters</p>
</div>
`;
} else {
fileList.innerHTML = '';
filteredFiles.forEach(file => {
const fileEl = document.createElement('div');
fileEl.className = `file-row px-5 py-3 border-b border-slate-100 grid grid-cols-12 gap-4 text-sm items-center transition-all ${
state.selectedFiles.includes(file) ? 'highlight' : ''
}`;
fileEl.innerHTML = `
<div class="col-span-5 flex items-center truncate">
<input
type="checkbox"
class="mr-3 h-4 w-4 text-indigo-600 rounded border-slate-300 focus:ring-indigo-500"
${state.selectedFiles.includes(file) ? 'checked' : ''}
data-file-id="${file.id}"
>
<div class="w-8 h-8 rounded-md flex items-center justify-center mr-2 ${
state.selectedFiles.includes(file) ? 'bg-indigo-50' : 'bg-slate-100'
}">
<i class="${getFileIcon(file)} text-slate-600"></i>
</div>
<div class="truncate">
<div class="font-medium text-slate-800 truncate">${file.name}</div>
<div class="text-xs text-slate-500 truncate">${formatTime(file.modified)}</div>
</div>
${file.isFavorite ? '<i class="fas fa-star text-yellow-400 ml-2 text-xs"></i>' : ''}
</div>
<div class="col-span-2 text-slate-600 truncate">
<span class="inline-flex items-center">
<i class="fas fa-user-circle mr-2 text-slate-400"></i>
${file.author || 'Unknown'}
</span>
</div>
<div class="col-span-2 text-slate-600 truncate text-sm">${file.type}</div>
<div class="col-span-2 text-slate-600 text-right text-sm">${formatFileSize(file.size)}</div>
<div class="col-span-1 text-slate-600 text-right text-sm">${formatDate(file.modified)}</div>
`;
// Add event listeners
const checkbox = fileEl.querySelector('input[type="checkbox"]');
checkbox.addEventListener('change', (e) => toggleFileSelection(file, e));
fileEl.addEventListener('dblclick', () => openFile(file));
// Add context menu
fileEl.addEventListener('contextmenu', (e) => {
e.preventDefault();
showContextMenu(file, e.clientX, e.clientY);
});
fileList.appendChild(fileEl);
});
}
// Update status bar
document.getElementById('statusCount').textContent =
state.selectedFiles.length > 0 ?
`${state.selectedFiles.length} selected of ${filteredFiles.length} items` :
`${filteredFiles.length} items`;
}
function showContextMenu(file, x, y) {
const contextMenu = document.getElementById('contextMenu');
state.contextMenuFile = file;
// Update context menu items based on file type
const favoriteItem = document.getElementById('contextFavorite');
if (file.isFavorite) {
favoriteItem.innerHTML = '<i class="fas fa-star"></i> Remove from Favorites';
} else {
favoriteItem.innerHTML = '<i class="fas fa-star"></i> Add to Favorites';
}
// Position the menu
contextMenu.style.left = `${x}px`;
contextMenu.style.top = `${y}px`;
contextMenu.style.display = 'block';
// Close menu when clicking elsewhere
const closeMenu = () => {
contextMenu.style.display = 'none';
document.removeEventListener('click', closeMenu);
};
setTimeout(() => {
document.addEventListener('click', closeMenu);
}, 100);
}
function updateSortIcons() {
// Reset all icons
document.getElementById('nameSortIcon').className = 'fas ml-1 fa-sort';
document.getElementById('authorSortIcon').className = 'fas ml-1 fa-sort';
document.getElementById('typeSortIcon').className = 'fas ml-1 fa-sort';
document.getElementById('sizeSortIcon').className = 'fas ml-1 fa-sort';
document.getElementById('modifiedSortIcon').className = 'fas ml-1 fa-sort';
// Set active sort icon
const activeIcon = document.getElementById(`${state.activeSort}SortIcon`);
if (activeIcon) {
activeIcon.className = `fas ml-1 fa-sort-${state.sortDirection === 'asc' ? 'up' : 'down'}`;
}
}
function updateDeleteButton() {
const deleteBtnContainer = document.getElementById('deleteBtnContainer');
const selectedCount = document.getElementById('selectedCount');
if (state.selectedFiles.length > 0) {
deleteBtnContainer.style.display = 'block';
selectedCount.textContent = state.selectedFiles.length;
} else {
deleteBtnContainer.style.display = 'none';
}
}
function updateSelectAllCheckbox() {
const selectAllCheckbox = document.getElementById('selectAllCheckbox');
const filteredFiles = filterFiles();
selectAllCheckbox.checked =
state.selectedFiles.length === filteredFiles.length &&
filteredFiles.length > 0;
}
// Core functions
function selectLocation(location) {
// Add to history
if (state.historyIndex === -1 ||
JSON.stringify(state.locationHistory[state.historyIndex]) !== JSON.stringify(location)) {
// If we're not at the end of history, truncate the future
if (state.historyIndex < state.locationHistory.length - 1) {
state.locationHistory = state.locationHistory.slice(0, state.historyIndex + 1);
}
state.locationHistory.push({...location});
state.historyIndex = state.locationHistory.length - 1;
}
state.currentLocation = location;
state.searchQuery = '';
state.selectedFiles = [];
updateFileList();
updateDeleteButton();
updateSelectAllCheckbox();
updateBreadcrumbs();
// Update back button state
updateBackForwardButtons();
}
function navigateBack() {
if (state.historyIndex > 0) {
state.historyIndex--;
state.currentLocation = {...state.locationHistory[state.historyIndex]};
updateFileList();
updateDeleteButton();
updateSelectAllCheckbox();
updateBreadcrumbs();
updateBackForwardButtons();
}
}
function navigateForward() {
if (state.historyIndex < state.locationHistory.length - 1) {
state.historyIndex++;
state.currentLocation = {...state.locationHistory[state.historyIndex]};
updateFileList();
updateDeleteButton();
updateSelectAllCheckbox();
updateBreadcrumbs();
updateBackForwardButtons();
}
}
function updateBackForwardButtons() {
const backBtn = document.getElementById('backBtn');
const forwardBtn = document.getElementById('forwardBtn');
if (state.historyIndex > 0) {
backBtn.disabled = false;
backBtn.classList.remove('opacity-50', 'cursor-not-allowed');
} else {
backBtn.disabled = true;
backBtn.classList.add('opacity-50', 'cursor-not-allowed');
}
if (state.historyIndex < state.locationHistory.length - 1) {
forwardBtn.disabled = false;
forwardBtn.classList.remove('opacity-50', 'cursor-not-allowed');
} else {
forwardBtn.disabled = true;
forwardBtn.classList.add('opacity-50', 'cursor-not-allowed');
}
}
function toggleIndexing() {
state.isIndexing = !state.isIndexing;
const indexBtn = document.getElementById('indexBtn');
const indexBtnText = document.getElementById('indexBtnText');
const indexStatus = document.getElementById('indexStatus');
if (state.isIndexing) {
indexBtn.classList.add('glow');
indexBtnText.textContent = 'Indexing...';
indexStatus.innerHTML = '<span class="text-indigo-600 font-medium flex items-center"><span class="w-2 h-2 rounded-full bg-indigo-500 mr-1.5 animate-pulse"></span>Running</span>';
let count = state.indexedCount;
const interval = setInterval(() => {
count += Math.floor(Math.random() * 10);
document.getElementById('indexedCount').textContent = count;
document.getElementById('lastIndexed').textContent = 'Just now';
if (!state.isIndexing) {
clearInterval(interval);
}
}, 3000);
setTimeout(() => {
if (Math.random() > 0.7) {
state.isIndexing = false;
indexBtn.classList.remove('glow');
indexBtnText.textContent = 'Index Now';
indexStatus.textContent = 'Idle';
showToast('Indexing completed successfully', 'success');
}
}, 15000);
} else {
indexBtn.classList.remove('glow');
indexBtnText.textContent = 'Index Now';
indexStatus.textContent = 'Idle';
}
}
function refreshFiles() {
state.performanceRating = ['fast', 'normal', 'slow'][Math.floor(Math.random() * 3)];
const performanceIcon = document.getElementById('performanceIcon');
const performanceText = document.getElementById('performanceText');
performanceText.textContent = `${state.performanceRating} performance`;
if (state.performanceRating === 'fast') {
performanceIcon.className = 'fas fa-circle text-xs mr-1.5 text-emerald-500';
} else if (state.performanceRating === 'normal') {
performanceIcon.className = 'fas fa-circle text-xs mr-1.5 text-amber-500';
} else {
performanceIcon.className = 'fas fa-circle text-xs mr-1.5 text-rose-500';
}
if (Math.random() > 0.7) {
const newFiles = [...state.files];
if (newFiles.length > 5 && Math.random() > 0.5) {
newFiles.splice(Math.floor(Math.random() * newFiles.length), 1);
} else {
const authors = ['John Doe', 'Jane Smith', 'Mike Johnson', 'Sarah Wilson', 'Emma Davis'];
const newFile = {
id: Math.max(...newFiles.map(f => f.id)) + 1,
name: ['New Document.docx', 'Report.pdf', 'Image.jpg', 'Archive.zip', 'Data.xlsx'][Math.floor(Math.random() * 5)],
path: [...state.currentLocation.path],
author: authors[Math.floor(Math.random() * authors.length)],
type: ['PDF Document', 'Word Document', 'Excel Spreadsheet', 'JPEG Image', 'ZIP Archive'][Math.floor(Math.random() * 5)],
size: Math.floor(Math.random() * 10000),
modified: new Date().toISOString(),
isFavorite: Math.random() > 0.8
};
newFiles.push(newFile);
}
state.files = newFiles;
updateFileList();
showToast('Files refreshed', 'success');
} else {
showToast('Files are up to date', 'info');
}
}
function sortBy(column) {
if (state.activeSort === column) {
state.sortDirection = state.sortDirection === 'asc' ? 'desc' : 'asc';
} else {
state.activeSort = column;
state.sortDirection = 'asc';
}
updateSortIcons();
updateFileList();
}
function toggleSelectAll(e) {
const allFiles = filterFiles();
if (e.target.checked) {
state.selectedFiles = [...allFiles];
} else {
state.selectedFiles = [];
}
updateFileList();
updateDeleteButton();
}
function toggleFileSelection(file, e) {
if (e.target.checked) {
state.selectedFiles.push(file);
} else {
const index = state.selectedFiles.findIndex(f => f.id === file.id);
if (index !== -1) {
state.selectedFiles.splice(index, 1);
}
}
updateDeleteButton();
updateSelectAllCheckbox();
}
function openFile(file) {
if (file.type === 'Folder') {
// Navigate to folder
const newLocation = {
id: Date.now(),
name
</html>