infinite-agents-public/src/ui_hybrid_8.html

945 lines
34 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Glass Morphism File Manager</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 25%, #f093fb 50%, #f5576c 75%, #4facfe 100%);
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
min-height: 100vh;
padding: 2rem;
color: #fff;
}
@keyframes gradientShift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
main {
max-width: 1400px;
margin: 0 auto;
}
h1 {
text-align: center;
font-size: 2.5rem;
font-weight: 300;
margin-bottom: 2rem;
text-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
background: linear-gradient(45deg, #ffffff, #f0f0f0, #ffffff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hybrid-component {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto 1fr;
gap: 1.5rem;
height: 80vh;
}
/* Glass Morphism Base Styles */
.glass-panel {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 20px;
padding: 2rem;
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.2),
inset 0 -1px 0 rgba(255, 255, 255, 0.1);
position: relative;
overflow: hidden;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.glass-panel::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transition: left 0.6s;
}
.glass-panel:hover::before {
left: 100%;
}
.glass-panel:hover {
transform: translateY(-2px);
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.15),
inset 0 1px 0 rgba(255, 255, 255, 0.3),
inset 0 -1px 0 rgba(255, 255, 255, 0.2);
}
/* Upload Area */
.upload-zone {
grid-column: 1 / -1;
border: 2px dashed rgba(255, 255, 255, 0.3);
border-radius: 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
min-height: 200px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.upload-zone.dragover {
border-color: rgba(255, 255, 255, 0.6);
background: rgba(255, 255, 255, 0.05);
transform: scale(1.02);
}
.upload-icon {
font-size: 3rem;
margin-bottom: 1rem;
opacity: 0.7;
}
.upload-text {
font-size: 1.2rem;
font-weight: 300;
margin-bottom: 0.5rem;
}
.upload-hint {
font-size: 0.9rem;
opacity: 0.7;
}
/* Progress Tracking */
.progress-panel {
display: flex;
flex-direction: column;
gap: 1rem;
}
.progress-item {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 1rem;
backdrop-filter: blur(10px);
transition: all 0.3s ease;
}
.progress-item:hover {
background: rgba(255, 255, 255, 0.08);
}
.progress-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.filename {
font-weight: 500;
truncate: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.file-size {
font-size: 0.8rem;
opacity: 0.7;
}
.progress-bar {
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 2px;
overflow: hidden;
position: relative;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #00f5ff, #0080ff);
border-radius: 2px;
transition: width 0.3s ease;
position: relative;
}
.progress-fill::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
/* File Browser */
.browser-panel {
display: flex;
flex-direction: column;
gap: 1rem;
}
.browser-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.search-glass {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 0.5rem 1rem;
color: #fff;
backdrop-filter: blur(10px);
width: 200px;
}
.search-glass::placeholder {
color: rgba(255, 255, 255, 0.6);
}
.search-glass:focus {
outline: none;
border-color: rgba(255, 255, 255, 0.4);
background: rgba(255, 255, 255, 0.15);
}
.file-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 1rem;
overflow-y: auto;
max-height: 400px;
}
.file-card {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 16px;
padding: 1rem;
text-align: center;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
backdrop-filter: blur(15px);
position: relative;
overflow: hidden;
}
.file-card::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
.file-card:hover::before {
opacity: 1;
}
.file-card:hover {
transform: translateY(-4px) scale(1.05);
background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 255, 255, 0.3);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}
.file-icon {
font-size: 2rem;
margin-bottom: 0.5rem;
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
}
.file-name {
font-size: 0.8rem;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Preview Modal */
.preview-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
animation: fadeIn 0.3s ease;
}
.preview-modal.active {
display: flex;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.preview-content {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(30px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 24px;
padding: 2rem;
max-width: 80vw;
max-height: 80vh;
overflow: auto;
position: relative;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}
.preview-close {
position: absolute;
top: 1rem;
right: 1rem;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
color: #fff;
font-size: 1.2rem;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
}
.preview-close:hover {
background: rgba(255, 255, 255, 0.2);
transform: scale(1.1);
}
/* Validation System */
.validation-panel {
display: flex;
flex-direction: column;
gap: 1rem;
}
.validation-item {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
backdrop-filter: blur(10px);
transition: all 0.3s ease;
}
.validation-icon {
font-size: 1.5rem;
min-width: 24px;
}
.validation-icon.success { color: #00ff88; }
.validation-icon.warning { color: #ffaa00; }
.validation-icon.error { color: #ff4444; }
.validation-text {
flex: 1;
}
.validation-title {
font-weight: 500;
margin-bottom: 0.25rem;
}
.validation-desc {
font-size: 0.8rem;
opacity: 0.7;
}
/* Responsive Design */
@media (max-width: 768px) {
.hybrid-component {
grid-template-columns: 1fr;
gap: 1rem;
}
h1 {
font-size: 2rem;
}
.glass-panel {
padding: 1.5rem;
}
.file-grid {
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}
}
/* Accessibility */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Focus States */
.file-card:focus,
.upload-zone:focus {
outline: 2px solid rgba(255, 255, 255, 0.5);
outline-offset: 2px;
}
/* Animation Classes */
.animate-in {
animation: slideInUp 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body>
<main>
<h1>File Manager - Glass Morphism Theme</h1>
<div class="hybrid-component">
<!-- Upload Zone -->
<div class="upload-zone glass-panel" id="uploadZone" tabindex="0" role="button" aria-label="File upload area">
<div class="upload-icon">☁️</div>
<div class="upload-text">Drop files here or click to browse</div>
<div class="upload-hint">Supports images, documents, and archives up to 50MB</div>
<input type="file" id="fileInput" multiple style="display: none;" accept="image/*,application/pdf,.doc,.docx,.txt,.zip,.rar">
</div>
<!-- Progress Tracking -->
<div class="progress-panel glass-panel">
<h3>📊 Upload Progress</h3>
<div id="progressContainer">
<div class="validation-item">
<div class="validation-icon">💫</div>
<div class="validation-text">
<div class="validation-title">Ready to upload</div>
<div class="validation-desc">Select files to see progress here</div>
</div>
</div>
</div>
</div>
<!-- File Browser -->
<div class="browser-panel glass-panel">
<div class="browser-header">
<h3>📁 File Browser</h3>
<input type="text" class="search-glass" placeholder="Search files..." id="searchInput">
</div>
<div class="file-grid" id="fileGrid">
<!-- Sample files -->
<div class="file-card" data-filename="document.pdf" data-type="pdf" tabindex="0">
<div class="file-icon">📄</div>
<div class="file-name">document.pdf</div>
</div>
<div class="file-card" data-filename="image.jpg" data-type="image" tabindex="0">
<div class="file-icon">🖼️</div>
<div class="file-name">image.jpg</div>
</div>
<div class="file-card" data-filename="archive.zip" data-type="archive" tabindex="0">
<div class="file-icon">📦</div>
<div class="file-name">archive.zip</div>
</div>
<div class="file-card" data-filename="presentation.pptx" data-type="presentation" tabindex="0">
<div class="file-icon">📊</div>
<div class="file-name">presentation.pptx</div>
</div>
<div class="file-card" data-filename="spreadsheet.xlsx" data-type="spreadsheet" tabindex="0">
<div class="file-icon">📈</div>
<div class="file-name">spreadsheet.xlsx</div>
</div>
<div class="file-card" data-filename="video.mp4" data-type="video" tabindex="0">
<div class="file-icon">🎬</div>
<div class="file-name">video.mp4</div>
</div>
</div>
</div>
<!-- Validation System -->
<div class="validation-panel glass-panel">
<h3>✅ File Validation</h3>
<div id="validationContainer">
<div class="validation-item">
<div class="validation-icon success"></div>
<div class="validation-text">
<div class="validation-title">System Ready</div>
<div class="validation-desc">All validation checks passed</div>
</div>
</div>
<div class="validation-item">
<div class="validation-icon success"></div>
<div class="validation-text">
<div class="validation-title">Storage Available</div>
<div class="validation-desc">2.4GB remaining space</div>
</div>
</div>
<div class="validation-item">
<div class="validation-icon success"></div>
<div class="validation-text">
<div class="validation-title">Security Scan</div>
<div class="validation-desc">Malware protection active</div>
</div>
</div>
</div>
</div>
</div>
<!-- Preview Modal -->
<div class="preview-modal" id="previewModal" role="dialog" aria-labelledby="previewTitle" aria-hidden="true">
<div class="preview-content">
<button class="preview-close" id="previewClose" aria-label="Close preview">×</button>
<h3 id="previewTitle">File Preview</h3>
<div id="previewBody">
<!-- Preview content will be inserted here -->
</div>
</div>
</div>
</main>
<script>
class GlassFileManager {
constructor() {
this.uploadZone = document.getElementById('uploadZone');
this.fileInput = document.getElementById('fileInput');
this.progressContainer = document.getElementById('progressContainer');
this.fileGrid = document.getElementById('fileGrid');
this.searchInput = document.getElementById('searchInput');
this.validationContainer = document.getElementById('validationContainer');
this.previewModal = document.getElementById('previewModal');
this.previewClose = document.getElementById('previewClose');
this.previewBody = document.getElementById('previewBody');
this.uploadedFiles = [];
this.isUploading = false;
this.initializeEventListeners();
this.initializeAnimation();
}
initializeEventListeners() {
// Upload zone events
this.uploadZone.addEventListener('click', () => this.fileInput.click());
this.uploadZone.addEventListener('dragover', this.handleDragOver.bind(this));
this.uploadZone.addEventListener('dragleave', this.handleDragLeave.bind(this));
this.uploadZone.addEventListener('drop', this.handleDrop.bind(this));
// File input change
this.fileInput.addEventListener('change', this.handleFileSelect.bind(this));
// Search functionality
this.searchInput.addEventListener('input', this.handleSearch.bind(this));
// File preview
this.fileGrid.addEventListener('click', this.handleFilePreview.bind(this));
this.previewClose.addEventListener('click', this.closePreview.bind(this));
this.previewModal.addEventListener('click', (e) => {
if (e.target === this.previewModal) this.closePreview();
});
// Keyboard navigation
document.addEventListener('keydown', this.handleKeyboard.bind(this));
}
initializeAnimation() {
// Animate panels on load
const panels = document.querySelectorAll('.glass-panel');
panels.forEach((panel, index) => {
setTimeout(() => {
panel.classList.add('animate-in');
}, index * 200);
});
}
handleDragOver(e) {
e.preventDefault();
this.uploadZone.classList.add('dragover');
}
handleDragLeave(e) {
e.preventDefault();
this.uploadZone.classList.remove('dragover');
}
handleDrop(e) {
e.preventDefault();
this.uploadZone.classList.remove('dragover');
const files = Array.from(e.dataTransfer.files);
this.processFiles(files);
}
handleFileSelect(e) {
const files = Array.from(e.target.files);
this.processFiles(files);
}
processFiles(files) {
if (this.isUploading) return;
// Validate files
const validatedFiles = this.validateFiles(files);
if (validatedFiles.length > 0) {
this.uploadFiles(validatedFiles);
}
}
validateFiles(files) {
const validFiles = [];
const validationResults = [];
files.forEach(file => {
const validation = this.validateSingleFile(file);
if (validation.valid) {
validFiles.push(file);
validationResults.push({
type: 'success',
icon: '✓',
title: `${file.name} validated`,
desc: `${this.formatFileSize(file.size)} - Ready to upload`
});
} else {
validationResults.push({
type: 'error',
icon: '✗',
title: `${file.name} rejected`,
desc: validation.reason
});
}
});
this.updateValidationDisplay(validationResults);
return validFiles;
}
validateSingleFile(file) {
const maxSize = 50 * 1024 * 1024; // 50MB
const allowedTypes = [
'image/jpeg', 'image/png', 'image/gif', 'image/webp',
'application/pdf', 'text/plain',
'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/zip', 'application/x-rar-compressed'
];
if (file.size > maxSize) {
return { valid: false, reason: 'File size exceeds 50MB limit' };
}
if (!allowedTypes.includes(file.type) && !this.isValidExtension(file.name)) {
return { valid: false, reason: 'File type not supported' };
}
return { valid: true };
}
isValidExtension(filename) {
const validExtensions = ['.txt', '.doc', '.docx', '.pdf', '.jpg', '.jpeg', '.png', '.gif', '.webp', '.zip', '.rar'];
return validExtensions.some(ext => filename.toLowerCase().endsWith(ext));
}
uploadFiles(files) {
this.isUploading = true;
this.progressContainer.innerHTML = '';
files.forEach((file, index) => {
this.uploadSingleFile(file, index);
});
}
uploadSingleFile(file, index) {
const progressItem = this.createProgressItem(file);
this.progressContainer.appendChild(progressItem);
// Simulate upload progress
let progress = 0;
const progressBar = progressItem.querySelector('.progress-fill');
const interval = setInterval(() => {
progress += Math.random() * 15;
if (progress >= 100) {
progress = 100;
clearInterval(interval);
this.completeUpload(file, progressItem);
}
progressBar.style.width = `${progress}%`;
}, 200);
}
createProgressItem(file) {
const item = document.createElement('div');
item.className = 'progress-item animate-in';
item.innerHTML = `
<div class="progress-header">
<span class="filename">${file.name}</span>
<span class="file-size">${this.formatFileSize(file.size)}</span>
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: 0%"></div>
</div>
`;
return item;
}
completeUpload(file, progressItem) {
this.uploadedFiles.push(file);
// Add to file browser
this.addToFileBrowser(file);
// Update validation
this.updateValidationDisplay([{
type: 'success',
icon: '✓',
title: 'Upload completed',
desc: `${file.name} successfully uploaded`
}]);
// Update progress item
setTimeout(() => {
progressItem.style.transform = 'scale(0.95)';
progressItem.style.opacity = '0.7';
}, 1000);
}
addToFileBrowser(file) {
const fileCard = document.createElement('div');
fileCard.className = 'file-card animate-in';
fileCard.setAttribute('data-filename', file.name);
fileCard.setAttribute('data-type', this.getFileType(file));
fileCard.setAttribute('tabindex', '0');
const icon = this.getFileIcon(file);
fileCard.innerHTML = `
<div class="file-icon">${icon}</div>
<div class="file-name">${file.name}</div>
`;
this.fileGrid.insertBefore(fileCard, this.fileGrid.firstChild);
}
getFileType(file) {
if (file.type.startsWith('image/')) return 'image';
if (file.type === 'application/pdf') return 'pdf';
if (file.type.includes('word')) return 'document';
if (file.type.includes('zip') || file.type.includes('rar')) return 'archive';
return 'file';
}
getFileIcon(file) {
const type = this.getFileType(file);
const icons = {
'image': '🖼️',
'pdf': '📄',
'document': '📝',
'archive': '📦',
'file': '📄'
};
return icons[type] || '📄';
}
handleSearch(e) {
const query = e.target.value.toLowerCase();
const fileCards = this.fileGrid.querySelectorAll('.file-card');
fileCards.forEach(card => {
const filename = card.getAttribute('data-filename').toLowerCase();
const visible = filename.includes(query);
card.style.display = visible ? 'block' : 'none';
if (visible && query) {
card.style.animation = 'none';
card.offsetHeight; // Trigger reflow
card.style.animation = 'slideInUp 0.3s ease';
}
});
}
handleFilePreview(e) {
const fileCard = e.target.closest('.file-card');
if (!fileCard) return;
const filename = fileCard.getAttribute('data-filename');
const filetype = fileCard.getAttribute('data-type');
this.showPreview(filename, filetype);
}
showPreview(filename, filetype) {
const previewTitle = document.getElementById('previewTitle');
previewTitle.textContent = `Preview: ${filename}`;
let content = '';
switch (filetype) {
case 'image':
content = `
<div style="text-align: center;">
<div style="width: 400px; height: 300px; background: rgba(255,255,255,0.1); border-radius: 16px; display: flex; align-items: center; justify-content: center; margin: 0 auto;">
<span style="font-size: 4rem;">🖼️</span>
</div>
<p style="margin-top: 1rem; opacity: 0.8;">Image preview would appear here</p>
</div>
`;
break;
case 'pdf':
content = `
<div style="text-align: center;">
<div style="width: 400px; height: 500px; background: rgba(255,255,255,0.1); border-radius: 16px; display: flex; align-items: center; justify-content: center; margin: 0 auto;">
<span style="font-size: 4rem;">📄</span>
</div>
<p style="margin-top: 1rem; opacity: 0.8;">PDF preview would appear here</p>
</div>
`;
break;
default:
content = `
<div style="text-align: center; padding: 2rem;">
<div style="font-size: 4rem; margin-bottom: 1rem;">📄</div>
<h4 style="margin-bottom: 1rem;">${filename}</h4>
<p style="opacity: 0.8;">Preview not available for this file type</p>
<p style="opacity: 0.6; font-size: 0.9rem; margin-top: 1rem;">File type: ${filetype}</p>
</div>
`;
}
this.previewBody.innerHTML = content;
this.previewModal.classList.add('active');
this.previewModal.setAttribute('aria-hidden', 'false');
// Focus management
this.previewClose.focus();
}
closePreview() {
this.previewModal.classList.remove('active');
this.previewModal.setAttribute('aria-hidden', 'true');
}
updateValidationDisplay(results) {
if (results.length === 0) return;
this.validationContainer.innerHTML = '';
results.forEach(result => {
const item = document.createElement('div');
item.className = 'validation-item animate-in';
item.innerHTML = `
<div class="validation-icon ${result.type}">${result.icon}</div>
<div class="validation-text">
<div class="validation-title">${result.title}</div>
<div class="validation-desc">${result.desc}</div>
</div>
`;
this.validationContainer.appendChild(item);
});
}
handleKeyboard(e) {
// Escape key closes preview
if (e.key === 'Escape' && this.previewModal.classList.contains('active')) {
this.closePreview();
}
// Enter key on file cards opens preview
if (e.key === 'Enter' && e.target.classList.contains('file-card')) {
this.handleFilePreview(e);
}
// Enter key on upload zone opens file picker
if (e.key === 'Enter' && e.target === this.uploadZone) {
this.fileInput.click();
}
}
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];
}
}
// Initialize the file manager when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
new GlassFileManager();
});
// Add some dynamic glass effects
document.addEventListener('mousemove', (e) => {
const panels = document.querySelectorAll('.glass-panel');
panels.forEach(panel => {
const rect = panel.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100;
panel.style.setProperty('--mouse-x', `${x}%`);
panel.style.setProperty('--mouse-y', `${y}%`);
});
});
</script>
</body>
</html>