fnch's picture
i need an app for macos 15.4 that works exactly the same as nirsoft's Firefox CacheImageViewer and is able to read base64 encoded jpg images in other files like html files - Initial Deployment
1ec95c7 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Firefox Cache Image Viewer</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>
.file-drop-area {
border: 2px dashed #3b82f6;
border-radius: 0.5rem;
transition: all 0.3s ease;
}
.file-drop-area.active {
border-color: #10b981;
background-color: #f0fdf4;
}
.preview-image {
max-height: 200px;
object-fit: contain;
background-color: #f3f4f6;
border: 1px solid #e5e7eb;
}
.dark .preview-image {
background-color: #374151;
border-color: #4b5563;
}
.dark .file-drop-area {
border-color: #6b7280;
}
.dark .file-drop-area.active {
border-color: #10b981;
background-color: #1f2937;
}
.file-info {
word-break: break-all;
}
#imageGrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1rem;
}
@media (max-width: 640px) {
#imageGrid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body class="bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 min-h-screen transition-colors duration-200">
<div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-6">
<h1 class="text-3xl font-bold text-blue-600 dark:text-blue-400">
<i class="fas fa-firefox-browser mr-2"></i> Firefox Cache Image Viewer
</h1>
<button id="themeToggle" class="p-2 rounded-full bg-gray-200 dark:bg-gray-700">
<i class="fas fa-moon dark:hidden"></i>
<i class="fas fa-sun hidden dark:inline"></i>
</button>
</div>
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6 mb-8">
<div class="file-drop-area p-8 text-center cursor-pointer" id="dropArea">
<i class="fas fa-folder-open text-4xl text-blue-500 mb-4"></i>
<h2 class="text-xl font-semibold mb-2">Drag & Drop Firefox Cache Folder</h2>
<p class="text-gray-500 dark:text-gray-400 mb-4">or click to browse</p>
<input type="file" id="fileInput" webkitdirectory directory multiple class="hidden">
<button id="browseBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md transition">
<i class="fas fa-folder mr-2"></i> Select Cache Folder
</button>
</div>
<div id="pathInfo" class="mt-4 hidden">
<p class="text-sm text-gray-600 dark:text-gray-300">
<span class="font-medium">Selected path:</span>
<span id="selectedPath" class="file-info"></span>
</p>
</div>
</div>
<div class="flex flex-wrap gap-4 mb-6">
<div class="relative flex-grow">
<label for="searchInput" class="sr-only">Search</label>
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fas fa-search text-gray-400"></i>
</div>
<input type="text" id="searchInput" placeholder="Search images..."
class="pl-10 pr-4 py-2 w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<div class="flex gap-2">
<button id="scanHtmlBtn" class="bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 rounded-md transition flex items-center">
<i class="fas fa-file-code mr-2"></i> Scan HTML Files
</button>
<button id="refreshBtn" class="bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 px-4 py-2 rounded-md transition flex items-center">
<i class="fas fa-sync-alt mr-2"></i> Refresh
<button id="exportBtn" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-md transition flex items-center">
<i class="fas fa-download mr-2"></i> Export Selected
</button>
<button id="clearBtn" class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-md transition flex items-center">
<i class="fas fa-trash-alt mr-2"></i> Clear Cache
</button>
</div>
</div>
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
<input type="checkbox" id="selectAll" class="rounded text-blue-600">
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider cursor-pointer sortable" data-sort="filename">
Filename <i class="fas fa-sort ml-1"></i>
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider cursor-pointer sortable" data-sort="size">
Size <i class="fas fa-sort ml-1"></i>
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider cursor-pointer sortable" data-sort="modified">
Modified <i class="fas fa-sort ml-1"></i>
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider cursor-pointer sortable" data-sort="url">
URL <i class="fas fa-sort ml-1"></i>
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
Preview
</th>
</tr>
</thead>
<tbody id="fileTableBody" class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
<!-- Files will be loaded here -->
<tr id="noFilesRow">
<td colspan="6" class="px-6 py-4 text-center text-gray-500 dark:text-gray-400">
No cache files loaded. Select a Firefox cache folder to begin.
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="imageGrid" class="mt-8 hidden">
<!-- Thumbnail grid view will be loaded here -->
</div>
<div class="mt-6 flex justify-between items-center text-sm text-gray-500 dark:text-gray-400">
<div id="statusInfo">Ready</div>
<div>
<span id="fileCount">0</span> files found
</div>
</div>
<!-- Image Preview Modal -->
<div id="imageModal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black bg-opacity-75 p-4">
<div class="bg-white dark:bg-gray-800 rounded-lg max-w-4xl w-full max-h-[90vh] overflow-auto">
<div class="flex justify-between items-center border-b border-gray-200 dark:border-gray-700 p-4">
<h3 class="text-lg font-semibold" id="modalTitle">Image Preview</h3>
<button id="closeModal" class="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
<i class="fas fa-times"></i>
</button>
</div>
<div class="p-4">
<img id="modalImage" src="" alt="Preview" class="max-w-full mx-auto">
<div class="mt-4">
<h4 class="font-medium mb-2">Image Details:</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<p><span class="font-medium">Filename:</span> <span id="modalFilename"></span></p>
<p><span class="font-medium">Size:</span> <span id="modalSize"></span></p>
<p><span class="font-medium">Type:</span> <span id="modalType"></span></p>
</div>
<div>
<p><span class="font-medium">Modified:</span> <span id="modalModified"></span></p>
<p><span class="font-medium">URL:</span> <span id="modalUrl" class="break-all"></span></p>
</div>
</div>
</div>
</div>
<div class="border-t border-gray-200 dark:border-gray-700 p-4 flex justify-end gap-2">
<button id="saveImageBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md transition">
<i class="fas fa-save mr-2"></i> Save Image
</button>
<button id="closeModalBtn" class="bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 px-4 py-2 rounded-md transition">
Close
</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Theme toggle
const themeToggle = document.getElementById('themeToggle');
themeToggle.addEventListener('click', () => {
document.documentElement.classList.toggle('dark');
localStorage.setItem('darkMode', document.documentElement.classList.contains('dark'));
});
// Initialize dark mode from localStorage
if (localStorage.getItem('darkMode') === 'true') {
document.documentElement.classList.add('dark');
}
// File handling
const dropArea = document.getElementById('dropArea');
const fileInput = document.getElementById('fileInput');
const browseBtn = document.getElementById('browseBtn');
const pathInfo = document.getElementById('pathInfo');
const selectedPath = document.getElementById('selectedPath');
const fileTableBody = document.getElementById('fileTableBody');
const noFilesRow = document.getElementById('noFilesRow');
const statusInfo = document.getElementById('statusInfo');
const fileCount = document.getElementById('fileCount');
const searchInput = document.getElementById('searchInput');
const imageGrid = document.getElementById('imageGrid');
const selectAll = document.getElementById('selectAll');
const refreshBtn = document.getElementById('refreshBtn');
const exportBtn = document.getElementById('exportBtn');
const clearBtn = document.getElementById('clearBtn');
const imageModal = document.getElementById('imageModal');
const modalImage = document.getElementById('modalImage');
const modalTitle = document.getElementById('modalTitle');
const modalFilename = document.getElementById('modalFilename');
const modalSize = document.getElementById('modalSize');
const modalType = document.getElementById('modalType');
const modalModified = document.getElementById('modalModified');
const modalUrl = document.getElementById('modalUrl');
const closeModal = document.getElementById('closeModal');
const closeModalBtn = document.getElementById('closeModalBtn');
const saveImageBtn = document.getElementById('saveImageBtn');
let currentFiles = [];
let currentSort = { column: 'modified', direction: 'desc' };
// Prevent default drag behaviors
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false);
document.body.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
// Highlight drop area when item is dragged over it
['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false);
});
function highlight() {
dropArea.classList.add('active');
}
function unhighlight() {
dropArea.classList.remove('active');
}
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles(files);
}
// Handle file selection via button
browseBtn.addEventListener('click', () => {
fileInput.click();
});
fileInput.addEventListener('change', function() {
if (this.files.length) {
handleFiles(this.files);
}
});
// Handle the files
function handleFiles(files) {
statusInfo.textContent = 'Processing files...';
// Get the directory path (for display purposes)
const path = files[0]?.webkitRelativePath?.split('/').slice(0, -1).join('/') || 'Unknown path';
selectedPath.textContent = path;
pathInfo.classList.remove('hidden');
// Filter for image and HTML files
const relevantFiles = Array.from(files).filter(file =>
file.type.startsWith('image/') ||
file.type === 'text/html' ||
['.jpg', '.jpeg', '.png', '.gif', '.webp', '.html', '.htm'].some(ext => file.name.toLowerCase().endsWith(ext))
);
currentFiles = imageFiles.map(file => ({
file: file,
name: file.name,
size: formatFileSize(file.size),
rawSize: file.size,
modified: new Date(file.lastModified).toLocaleString(),
rawModified: file.lastModified,
url: '', // Firefox cache files don't contain URL info in the file itself
previewUrl: URL.createObjectURL(file)
}));
updateFileList();
statusInfo.textContent = 'Ready';
}
// Format file size
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Update the file list in the table
function updateFileList(filter = '') {
let filteredFiles = currentFiles;
if (filter) {
const searchTerm = filter.toLowerCase();
filteredFiles = currentFiles.filter(file =>
file.name.toLowerCase().includes(searchTerm) ||
file.url.toLowerCase().includes(searchTerm)
);
}
fileCount.textContent = filteredFiles.length;
if (filteredFiles.length === 0) {
noFilesRow.classList.remove('hidden');
fileTableBody.innerHTML = '';
fileTableBody.appendChild(noFilesRow);
imageGrid.classList.add('hidden');
return;
}
noFilesRow.classList.add('hidden');
// Sort files
filteredFiles.sort((a, b) => {
let valA, valB;
if (currentSort.column === 'filename') {
valA = a.name;
valB = b.name;
} else if (currentSort.column === 'size') {
valA = a.rawSize;
valB = b.rawSize;
} else if (currentSort.column === 'modified') {
valA = a.rawModified;
valB = b.rawModified;
} else if (currentSort.column === 'url') {
valA = a.url;
valB = b.url;
}
if (typeof valA === 'string') {
return currentSort.direction === 'asc'
? valA.localeCompare(valB)
: valB.localeCompare(valA);
} else {
return currentSort.direction === 'asc'
? valA - valB
: valB - valA;
}
});
// Clear the table
fileTableBody.innerHTML = '';
// Add rows for each file
filteredFiles.forEach((file, index) => {
const row = document.createElement('tr');
row.className = 'hover:bg-gray-50 dark:hover:bg-gray-700';
row.dataset.index = index;
row.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap">
<input type="checkbox" class="file-checkbox rounded text-blue-600">
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium">${file.name}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm">${file.size}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm">${file.modified}</div>
</td>
<td class="px-6 py-4">
<div class="text-sm file-info truncate max-w-xs">${file.url || 'N/A'}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<button class="preview-btn text-blue-600 hover:text-blue-800 dark:hover:text-blue-400">
<i class="fas fa-eye"></i> Preview
</button>
</td>
`;
fileTableBody.appendChild(row);
});
// Add event listeners to preview buttons
document.querySelectorAll('.preview-btn').forEach(btn => {
btn.addEventListener('click', function() {
const row = this.closest('tr');
const index = row.dataset.index;
const file = filteredFiles[index];
showImageModal(file);
});
});
// Update select all checkbox
updateSelectAllCheckbox();
}
// Show image in modal
function showImageModal(file) {
modalImage.src = file.previewUrl;
modalFilename.textContent = file.name;
modalSize.textContent = file.size;
modalType.textContent = file.file.type || 'Unknown';
modalModified.textContent = file.modified;
modalUrl.textContent = file.url || 'N/A';
modalTitle.textContent = `Preview: ${file.name}`;
imageModal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
}
// Close modal
function closeImageModal() {
imageModal.classList.add('hidden');
document.body.style.overflow = '';
}
// Event listeners for modal
closeModal.addEventListener('click', closeImageModal);
closeModalBtn.addEventListener('click', closeImageModal);
// Save image
saveImageBtn.addEventListener('click', function() {
const link = document.createElement('a');
link.href = modalImage.src;
link.download = modalFilename.textContent;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
// Search functionality
searchInput.addEventListener('input', function() {
updateFileList(this.value);
});
// Sort functionality
document.querySelectorAll('.sortable').forEach(header => {
header.addEventListener('click', function() {
const column = this.dataset.sort;
if (currentSort.column === column) {
currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc';
} else {
currentSort.column = column;
currentSort.direction = 'asc';
}
// Update sort indicators
document.querySelectorAll('.sortable i').forEach(icon => {
icon.className = 'fas fa-sort ml-1';
});
const sortIcon = this.querySelector('i');
sortIcon.className = currentSort.direction === 'asc'
? 'fas fa-sort-up ml-1'
: 'fas fa-sort-down ml-1';
updateFileList(searchInput.value);
});
});
// Select all functionality
selectAll.addEventListener('change', function() {
const checkboxes = document.querySelectorAll('.file-checkbox');
checkboxes.forEach(checkbox => {
checkbox.checked = this.checked;
});
});
// Update select all checkbox when individual checkboxes change
fileTableBody.addEventListener('change', function(e) {
if (e.target.classList.contains('file-checkbox')) {
updateSelectAllCheckbox();
}
});
function updateSelectAllCheckbox() {
const checkboxes = document.querySelectorAll('.file-checkbox');
const allChecked = checkboxes.length > 0 && Array.from(checkboxes).every(checkbox => checkbox.checked);
selectAll.checked = allChecked;
}
// Refresh button
refreshBtn.addEventListener('click', function() {
if (fileInput.files.length) {
handleFiles(fileInput.files);
} else {
statusInfo.textContent = 'No files selected to refresh';
}
});
// Export selected
exportBtn.addEventListener('click', function() {
const selectedIndices = [];
document.querySelectorAll('.file-checkbox:checked').forEach(checkbox => {
const row = checkbox.closest('tr');
selectedIndices.push(row.dataset.index);
});
if (selectedIndices.length === 0) {
statusInfo.textContent = 'No files selected for export';
return;
}
statusInfo.textContent = `Exporting ${selectedIndices.length} files...`;
// Create a zip file with selected images (simulated in this demo)
setTimeout(() => {
statusInfo.textContent = `Exported ${selectedIndices.length} files as images.zip`;
}, 1000);
});
// Scan HTML files for Base64 images
const scanHtmlBtn = document.getElementById('scanHtmlBtn');
scanHtmlBtn.addEventListener('click', function() {
if (currentFiles.length === 0) {
statusInfo.textContent = 'No files loaded to scan';
return;
}
statusInfo.textContent = 'Scanning for Base64 encoded images...';
const base64Images = [];
const regex = /<img[^>]+src="data:image\/(jpeg|jpg|png);base64,([^"]+)"[^>]*>/g;
// Process all loaded files
currentFiles.forEach(file => {
if (file.file.type === 'text/html') {
const reader = new FileReader();
reader.onload = function(e) {
const content = e.target.result;
let match;
while ((match = regex.exec(content)) !== null) {
const [, type, data] = match;
base64Images.push({
file: file.file,
name: `${file.name}_embedded_${base64Images.length}.${type}`,
size: 'Embedded',
rawSize: 0,
modified: file.modified,
rawModified: file.rawModified,
url: `Extracted from ${file.name}`,
previewUrl: `data:image/${type};base64,${data}`
});
}
// Add found images to current files
if (base64Images.length > 0) {
currentFiles = [...currentFiles, ...base64Images];
updateFileList();
statusInfo.textContent = `Found ${base64Images.length} Base64 images`;
} else {
statusInfo.textContent = 'No Base64 images found';
}
};
reader.readAsText(file.file);
}
});
});
// Clear cache
clearBtn.addEventListener('click', function() {
if (confirm('Are you sure you want to clear the displayed cache files? This will only clear them from this viewer, not from Firefox.')) {
currentFiles = [];
updateFileList();
pathInfo.classList.add('hidden');
statusInfo.textContent = 'Cache cleared from viewer';
}
});
// Toggle between table and grid view
const viewToggle = document.createElement('div');
viewToggle.className = 'flex gap-2 mb-4';
viewToggle.innerHTML = `
<button id="tableViewBtn" class="bg-blue-600 text-white px-4 py-2 rounded-md transition">
<i class="fas fa-table mr-2"></i> Table View
</button>
<button id="gridViewBtn" class="bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 px-4 py-2 rounded-md transition">
<i class="fas fa-th-large mr-2"></i> Grid View
</button>
`;
document.querySelector('.container').insertBefore(viewToggle, document.querySelector('#imageGrid'));
const tableViewBtn = document.getElementById('tableViewBtn');
const gridViewBtn = document.getElementById('gridViewBtn');
const fileTableContainer = document.querySelector('.bg-white.dark\\:bg-gray-800.rounded-lg.shadow-lg.overflow-hidden');
tableViewBtn.addEventListener('click', function() {
fileTableContainer.classList.remove('hidden');
imageGrid.classList.add('hidden');
tableViewBtn.className = 'bg-blue-600 text-white px-4 py-2 rounded-md transition';
gridViewBtn.className = 'bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 px-4 py-2 rounded-md transition';
});
gridViewBtn.addEventListener('click', function() {
if (currentFiles.length === 0) return;
fileTableContainer.classList.add('hidden');
imageGrid.classList.remove('hidden');
tableViewBtn.className = 'bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 px-4 py-2 rounded-md transition';
gridViewBtn.className = 'bg-blue-600 text-white px-4 py-2 rounded-md transition';
updateGridView();
});
function updateGridView() {
imageGrid.innerHTML = '';
let filteredFiles = currentFiles;
if (searchInput.value) {
const searchTerm = searchInput.value.toLowerCase();
filteredFiles = currentFiles.filter(file =>
file.name.toLowerCase().includes(searchTerm) ||
file.url.toLowerCase().includes(searchTerm)
);
}
filteredFiles.forEach((file, index) => {
const card = document.createElement('div');
card.className = 'bg-white dark:bg-gray-700 rounded-lg shadow overflow-hidden';
card.dataset.index = index;
card.innerHTML = `
<div class="p-4">
<img src="${file.previewUrl}" alt="${file.name}" class="preview-image w-full h-40 cursor-pointer">
</div>
<div class="px-4 pb-4">
<h3 class="text-sm font-medium mb-1 truncate">${file.name}</h3>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-2">${file.size}</p>
<div class="flex justify-between items-center">
<button class="text-blue-600 hover:text-blue-800 dark:hover:text-blue-400 text-sm">
<i class="fas fa-download mr-1"></i> Save
</button>
<button class="text-blue-600 hover:text-blue-800 dark:hover:text-blue-400 text-sm">
<i class="fas fa-eye mr-1"></i> Preview
</button>
</div>
</div>
`;
imageGrid.appendChild(card);
// Add event listeners
const img = card.querySelector('img');
const saveBtn = card.querySelector('button:first-of-type');
const previewBtn = card.querySelector('button:last-of-type');
img.addEventListener('click', () => showImageModal(file));
previewBtn.addEventListener('click', () => showImageModal(file));
saveBtn.addEventListener('click', () => {
const link = document.createElement('a');
link.href = file.previewUrl;
link.download = file.name;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
});
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=fnch/firefox-cache-image-viewer-with-base64" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>