infinite-agents-public/src_infinite/ui_hybrid_1.html

1061 lines
34 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cyberpunk Data Explorer - Neural Implant Registry</title>
<style>
:root {
--neon-cyan: #00ffff;
--neon-pink: #ff00ff;
--neon-yellow: #ffff00;
--neon-green: #00ff00;
--dark-bg: #0a0a0a;
--darker-bg: #050505;
--panel-bg: rgba(10, 10, 10, 0.9);
--text-primary: #ffffff;
--text-secondary: #a0a0a0;
--border-color: #303030;
--hover-bg: rgba(0, 255, 255, 0.1);
--selection-bg: rgba(255, 0, 255, 0.2);
--glitch-1: #ff00ff;
--glitch-2: #00ffff;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Courier New', monospace;
background: var(--dark-bg);
color: var(--text-primary);
overflow-x: hidden;
min-height: 100vh;
position: relative;
}
/* Animated background grid */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
repeating-linear-gradient(0deg, transparent, transparent 50px, rgba(0, 255, 255, 0.03) 50px, rgba(0, 255, 255, 0.03) 51px),
repeating-linear-gradient(90deg, transparent, transparent 50px, rgba(255, 0, 255, 0.03) 50px, rgba(255, 0, 255, 0.03) 51px);
pointer-events: none;
z-index: 1;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
position: relative;
z-index: 2;
}
/* Header with glitch effect */
.header {
text-align: center;
margin-bottom: 40px;
position: relative;
}
.header h1 {
font-size: 3rem;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.2em;
position: relative;
animation: flicker 2s infinite alternate;
}
.header h1::before,
.header h1::after {
content: attr(data-text);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.header h1::before {
animation: glitch-1 0.3s infinite;
color: var(--glitch-1);
z-index: -1;
}
.header h1::after {
animation: glitch-2 0.3s infinite;
color: var(--glitch-2);
z-index: -2;
}
@keyframes flicker {
0%, 100% { opacity: 1; }
50% { opacity: 0.8; }
}
@keyframes glitch-1 {
0% { transform: translateX(0); }
20% { transform: translateX(-2px); }
40% { transform: translateX(2px); }
60% { transform: translateX(-1px); }
80% { transform: translateX(1px); }
100% { transform: translateX(0); }
}
@keyframes glitch-2 {
0% { transform: translateX(0); }
20% { transform: translateX(2px); }
40% { transform: translateX(-2px); }
60% { transform: translateX(1px); }
80% { transform: translateX(-1px); }
100% { transform: translateX(0); }
}
/* Control panel */
.control-panel {
background: var(--panel-bg);
border: 1px solid var(--border-color);
border-radius: 0;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 0 20px rgba(0, 255, 255, 0.3);
position: relative;
overflow: hidden;
}
.control-panel::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(0, 255, 255, 0.2), transparent);
animation: scan 3s infinite;
}
@keyframes scan {
0% { left: -100%; }
100% { left: 100%; }
}
.controls-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
position: relative;
z-index: 1;
}
.control-group {
display: flex;
flex-direction: column;
gap: 10px;
}
.control-group label {
color: var(--neon-cyan);
text-transform: uppercase;
font-size: 0.8rem;
letter-spacing: 0.1em;
}
input[type="text"],
select {
background: rgba(0, 0, 0, 0.5);
border: 1px solid var(--border-color);
color: var(--text-primary);
padding: 10px;
font-family: inherit;
transition: all 0.3s;
}
input[type="text"]:focus,
select:focus {
outline: none;
border-color: var(--neon-cyan);
box-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
}
.button {
background: transparent;
border: 1px solid var(--neon-pink);
color: var(--neon-pink);
padding: 10px 20px;
cursor: pointer;
text-transform: uppercase;
letter-spacing: 0.1em;
transition: all 0.3s;
position: relative;
overflow: hidden;
}
.button::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: var(--neon-pink);
transform: translate(-50%, -50%);
transition: all 0.3s;
}
.button:hover {
color: var(--dark-bg);
box-shadow: 0 0 20px var(--neon-pink);
}
.button:hover::before {
width: 100%;
height: 100%;
}
.button span {
position: relative;
z-index: 1;
}
/* Data table */
.data-table-container {
background: var(--panel-bg);
border: 1px solid var(--border-color);
overflow: hidden;
position: relative;
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
border-bottom: 1px solid var(--border-color);
background: rgba(0, 255, 255, 0.05);
}
.table-title {
color: var(--neon-cyan);
font-size: 1.2rem;
text-transform: uppercase;
letter-spacing: 0.1em;
}
.table-actions {
display: flex;
gap: 10px;
}
.table-wrapper {
overflow-x: auto;
max-height: 600px;
overflow-y: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
th {
background: rgba(0, 0, 0, 0.5);
padding: 15px;
text-align: left;
color: var(--neon-cyan);
text-transform: uppercase;
font-size: 0.8rem;
letter-spacing: 0.1em;
position: sticky;
top: 0;
z-index: 10;
cursor: pointer;
user-select: none;
}
th:hover {
background: rgba(0, 255, 255, 0.1);
}
th.sorted-asc::after {
content: ' ▲';
color: var(--neon-yellow);
}
th.sorted-desc::after {
content: ' ▼';
color: var(--neon-yellow);
}
td {
padding: 15px;
border-bottom: 1px solid rgba(48, 48, 48, 0.5);
}
tr {
transition: all 0.3s;
}
tbody tr:hover {
background: var(--hover-bg);
transform: translateX(5px);
}
tbody tr.selected {
background: var(--selection-bg);
box-shadow: 0 0 10px rgba(255, 0, 255, 0.3);
}
.checkbox-cell {
width: 50px;
}
.cyber-checkbox {
width: 20px;
height: 20px;
appearance: none;
border: 2px solid var(--neon-pink);
background: transparent;
cursor: pointer;
position: relative;
transition: all 0.3s;
}
.cyber-checkbox:checked {
background: var(--neon-pink);
box-shadow: 0 0 10px var(--neon-pink);
}
.cyber-checkbox:checked::after {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: var(--dark-bg);
font-weight: bold;
}
/* Status indicators */
.status {
padding: 5px 10px;
border-radius: 3px;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.05em;
display: inline-block;
}
.status.active {
background: rgba(0, 255, 0, 0.2);
border: 1px solid var(--neon-green);
color: var(--neon-green);
box-shadow: 0 0 5px rgba(0, 255, 0, 0.5);
}
.status.inactive {
background: rgba(255, 0, 0, 0.2);
border: 1px solid #ff0000;
color: #ff0000;
}
.status.pending {
background: rgba(255, 255, 0, 0.2);
border: 1px solid var(--neon-yellow);
color: var(--neon-yellow);
animation: pulse 1s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
/* Risk level indicators */
.risk-level {
display: flex;
align-items: center;
gap: 5px;
}
.risk-bar {
width: 60px;
height: 10px;
background: rgba(255, 255, 255, 0.1);
position: relative;
overflow: hidden;
}
.risk-fill {
height: 100%;
transition: width 0.3s;
}
.risk-low { background: var(--neon-green); }
.risk-medium { background: var(--neon-yellow); }
.risk-high { background: #ff0000; }
/* Pagination */
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
padding: 20px;
border-top: 1px solid var(--border-color);
}
.page-button {
background: transparent;
border: 1px solid var(--border-color);
color: var(--text-secondary);
padding: 8px 12px;
cursor: pointer;
transition: all 0.3s;
}
.page-button:hover:not(:disabled) {
border-color: var(--neon-cyan);
color: var(--neon-cyan);
box-shadow: 0 0 10px rgba(0, 255, 255, 0.3);
}
.page-button:disabled {
opacity: 0.3;
cursor: not-allowed;
}
.page-button.active {
background: var(--neon-cyan);
color: var(--dark-bg);
border-color: var(--neon-cyan);
box-shadow: 0 0 15px var(--neon-cyan);
}
.page-info {
color: var(--text-secondary);
font-size: 0.9rem;
}
/* Export modal */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
z-index: 1000;
backdrop-filter: blur(5px);
}
.modal.active {
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background: var(--panel-bg);
border: 1px solid var(--neon-cyan);
padding: 30px;
max-width: 500px;
width: 90%;
box-shadow: 0 0 30px rgba(0, 255, 255, 0.5);
position: relative;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-title {
color: var(--neon-cyan);
font-size: 1.5rem;
text-transform: uppercase;
letter-spacing: 0.1em;
}
.close-button {
background: transparent;
border: none;
color: var(--neon-pink);
font-size: 2rem;
cursor: pointer;
transition: all 0.3s;
}
.close-button:hover {
transform: rotate(90deg);
text-shadow: 0 0 10px var(--neon-pink);
}
.export-options {
display: flex;
flex-direction: column;
gap: 15px;
margin-bottom: 20px;
}
.export-option {
display: flex;
align-items: center;
gap: 10px;
padding: 15px;
border: 1px solid var(--border-color);
cursor: pointer;
transition: all 0.3s;
}
.export-option:hover {
border-color: var(--neon-cyan);
background: rgba(0, 255, 255, 0.05);
}
.export-option input[type="radio"] {
appearance: none;
width: 20px;
height: 20px;
border: 2px solid var(--neon-cyan);
border-radius: 50%;
position: relative;
}
.export-option input[type="radio"]:checked::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 10px;
height: 10px;
background: var(--neon-cyan);
border-radius: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 10px var(--neon-cyan);
}
/* Loading animation */
.loading {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2000;
}
.loading.active {
display: block;
}
.loading-spinner {
width: 50px;
height: 50px;
border: 3px solid transparent;
border-top-color: var(--neon-cyan);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Responsive design */
@media (max-width: 768px) {
.header h1 {
font-size: 2rem;
}
.controls-grid {
grid-template-columns: 1fr;
}
.table-wrapper {
max-height: 400px;
}
th, td {
padding: 10px;
font-size: 0.9rem;
}
}
/* 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 styles */
*:focus-visible {
outline: 2px solid var(--neon-cyan);
outline-offset: 2px;
}
</style>
</head>
<body>
<div class="container">
<header class="header">
<h1 data-text="NEURAL IMPLANT REGISTRY">NEURAL IMPLANT REGISTRY</h1>
<p style="color: var(--text-secondary); margin-top: 10px;">CyberCorp Industries - Classified Database Access Terminal</p>
</header>
<div class="control-panel">
<div class="controls-grid">
<div class="control-group">
<label for="search">Search Database</label>
<input type="text" id="search" placeholder="Enter subject ID, name, or implant model...">
</div>
<div class="control-group">
<label for="statusFilter">Status Filter</label>
<select id="statusFilter">
<option value="">All Statuses</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
<option value="pending">Pending</option>
</select>
</div>
<div class="control-group">
<label for="riskFilter">Risk Level</label>
<select id="riskFilter">
<option value="">All Risk Levels</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
</div>
<div class="control-group">
<label for="sortBy">Sort By</label>
<select id="sortBy">
<option value="id">Subject ID</option>
<option value="name">Name</option>
<option value="date">Installation Date</option>
<option value="risk">Risk Level</option>
</select>
</div>
</div>
</div>
<div class="data-table-container">
<div class="table-header">
<span class="table-title">Registered Subjects</span>
<div class="table-actions">
<button class="button" id="selectAllBtn">
<span>Select All</span>
</button>
<button class="button" id="exportBtn">
<span>Export Data</span>
</button>
</div>
</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th class="checkbox-cell">
<span class="sr-only">Select</span>
</th>
<th data-sort="id">Subject ID</th>
<th data-sort="name">Name</th>
<th data-sort="model">Implant Model</th>
<th data-sort="date">Installation Date</th>
<th data-sort="status">Status</th>
<th data-sort="risk">Risk Level</th>
<th>Neural Sync</th>
</tr>
</thead>
<tbody id="dataTable">
<!-- Data will be populated here -->
</tbody>
</table>
</div>
<div class="pagination" id="pagination">
<!-- Pagination will be populated here -->
</div>
</div>
</div>
<!-- Export Modal -->
<div class="modal" id="exportModal">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Export Data</h2>
<button class="close-button" id="closeModal">&times;</button>
</div>
<div class="export-options">
<label class="export-option">
<input type="radio" name="exportFormat" value="json" checked>
<span>JSON - Neural Network Format</span>
</label>
<label class="export-option">
<input type="radio" name="exportFormat" value="csv">
<span>CSV - Legacy Systems Compatible</span>
</label>
<label class="export-option">
<input type="radio" name="exportFormat" value="xml">
<span>XML - Corporate Database Format</span>
</label>
</div>
<button class="button" id="confirmExport" style="width: 100%;">
<span>Initialize Data Transfer</span>
</button>
</div>
</div>
<!-- Loading indicator -->
<div class="loading" id="loading">
<div class="loading-spinner"></div>
</div>
<script>
// Sample data generator
const generateData = () => {
const firstNames = ['Kaida', 'Zane', 'Nyx', 'Rex', 'Luna', 'Axel', 'Nova', 'Jax', 'Echo', 'Vega'];
const lastNames = ['Chen', 'Nakamura', 'Volkov', 'Sterling', 'Cross', 'Black', 'Phoenix', 'Cipher', 'Shadow', 'Zero'];
const implantModels = ['CX-7000', 'NeuralLink Pro', 'SynapseBoost X', 'CortexPlus 2.0', 'MindBridge Alpha', 'CerebrumMax'];
const statuses = ['active', 'inactive', 'pending'];
const riskLevels = ['low', 'medium', 'high'];
const data = [];
for (let i = 1; i <= 150; i++) {
data.push({
id: `CYB-${String(i).padStart(6, '0')}`,
name: `${firstNames[Math.floor(Math.random() * firstNames.length)]} ${lastNames[Math.floor(Math.random() * lastNames.length)]}`,
model: implantModels[Math.floor(Math.random() * implantModels.length)],
date: new Date(2020 + Math.floor(Math.random() * 5), Math.floor(Math.random() * 12), Math.floor(Math.random() * 28) + 1).toISOString().split('T')[0],
status: statuses[Math.floor(Math.random() * statuses.length)],
risk: riskLevels[Math.floor(Math.random() * riskLevels.length)],
sync: Math.floor(Math.random() * 100)
});
}
return data;
};
// Application state
let allData = generateData();
let filteredData = [...allData];
let currentPage = 1;
const itemsPerPage = 10;
let selectedRows = new Set();
let sortColumn = 'id';
let sortDirection = 'asc';
// DOM elements
const searchInput = document.getElementById('search');
const statusFilter = document.getElementById('statusFilter');
const riskFilter = document.getElementById('riskFilter');
const sortBySelect = document.getElementById('sortBy');
const dataTable = document.getElementById('dataTable');
const pagination = document.getElementById('pagination');
const selectAllBtn = document.getElementById('selectAllBtn');
const exportBtn = document.getElementById('exportBtn');
const exportModal = document.getElementById('exportModal');
const closeModal = document.getElementById('closeModal');
const confirmExport = document.getElementById('confirmExport');
const loading = document.getElementById('loading');
// Filter and search functionality
const applyFilters = () => {
const searchTerm = searchInput.value.toLowerCase();
const statusValue = statusFilter.value;
const riskValue = riskFilter.value;
filteredData = allData.filter(item => {
const matchesSearch = !searchTerm ||
item.id.toLowerCase().includes(searchTerm) ||
item.name.toLowerCase().includes(searchTerm) ||
item.model.toLowerCase().includes(searchTerm);
const matchesStatus = !statusValue || item.status === statusValue;
const matchesRisk = !riskValue || item.risk === riskValue;
return matchesSearch && matchesStatus && matchesRisk;
});
currentPage = 1;
renderTable();
};
// Sorting functionality
const sortData = (column) => {
if (sortColumn === column) {
sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
} else {
sortColumn = column;
sortDirection = 'asc';
}
filteredData.sort((a, b) => {
let aVal = a[column];
let bVal = b[column];
if (column === 'risk') {
const riskOrder = { low: 1, medium: 2, high: 3 };
aVal = riskOrder[aVal];
bVal = riskOrder[bVal];
}
if (sortDirection === 'asc') {
return aVal > bVal ? 1 : -1;
} else {
return aVal < bVal ? 1 : -1;
}
});
renderTable();
};
// Render table
const renderTable = () => {
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
const pageData = filteredData.slice(startIndex, endIndex);
// Update sort indicators
document.querySelectorAll('th[data-sort]').forEach(th => {
th.classList.remove('sorted-asc', 'sorted-desc');
if (th.dataset.sort === sortColumn) {
th.classList.add(`sorted-${sortDirection}`);
}
});
dataTable.innerHTML = pageData.map(item => {
const isSelected = selectedRows.has(item.id);
const riskWidth = item.risk === 'low' ? 30 : item.risk === 'medium' ? 60 : 90;
return `
<tr class="${isSelected ? 'selected' : ''}" data-id="${item.id}">
<td class="checkbox-cell">
<input type="checkbox" class="cyber-checkbox" ${isSelected ? 'checked' : ''} aria-label="Select ${item.name}">
</td>
<td>${item.id}</td>
<td>${item.name}</td>
<td>${item.model}</td>
<td>${item.date}</td>
<td><span class="status ${item.status}">${item.status}</span></td>
<td>
<div class="risk-level">
<div class="risk-bar">
<div class="risk-fill risk-${item.risk}" style="width: ${riskWidth}%"></div>
</div>
<span>${item.risk.toUpperCase()}</span>
</div>
</td>
<td>${item.sync}%</td>
</tr>
`;
}).join('');
renderPagination();
};
// Render pagination
const renderPagination = () => {
const totalPages = Math.ceil(filteredData.length / itemsPerPage);
const pages = [];
// Previous button
pages.push(`
<button class="page-button" ${currentPage === 1 ? 'disabled' : ''} data-page="${currentPage - 1}">
&lt;
</button>
`);
// Page numbers
let startPage = Math.max(1, currentPage - 2);
let endPage = Math.min(totalPages, startPage + 4);
if (endPage - startPage < 4) {
startPage = Math.max(1, endPage - 4);
}
for (let i = startPage; i <= endPage; i++) {
pages.push(`
<button class="page-button ${i === currentPage ? 'active' : ''}" data-page="${i}">
${i}
</button>
`);
}
// Next button
pages.push(`
<button class="page-button" ${currentPage === totalPages ? 'disabled' : ''} data-page="${currentPage + 1}">
&gt;
</button>
`);
// Page info
pages.push(`
<span class="page-info">
${filteredData.length} records | Page ${currentPage} of ${totalPages}
</span>
`);
pagination.innerHTML = pages.join('');
};
// Event listeners
searchInput.addEventListener('input', applyFilters);
statusFilter.addEventListener('change', applyFilters);
riskFilter.addEventListener('change', applyFilters);
sortBySelect.addEventListener('change', (e) => sortData(e.target.value));
// Table header sorting
document.querySelectorAll('th[data-sort]').forEach(th => {
th.addEventListener('click', () => sortData(th.dataset.sort));
});
// Row selection
dataTable.addEventListener('click', (e) => {
if (e.target.classList.contains('cyber-checkbox')) {
const row = e.target.closest('tr');
const id = row.dataset.id;
if (e.target.checked) {
selectedRows.add(id);
row.classList.add('selected');
} else {
selectedRows.delete(id);
row.classList.remove('selected');
}
}
});
// Select all functionality
selectAllBtn.addEventListener('click', () => {
const checkboxes = dataTable.querySelectorAll('.cyber-checkbox');
const allChecked = Array.from(checkboxes).every(cb => cb.checked);
checkboxes.forEach(cb => {
const row = cb.closest('tr');
const id = row.dataset.id;
if (allChecked) {
cb.checked = false;
selectedRows.delete(id);
row.classList.remove('selected');
} else {
cb.checked = true;
selectedRows.add(id);
row.classList.add('selected');
}
});
selectAllBtn.querySelector('span').textContent = allChecked ? 'Select All' : 'Deselect All';
});
// Pagination clicks
pagination.addEventListener('click', (e) => {
if (e.target.classList.contains('page-button') && !e.target.disabled) {
currentPage = parseInt(e.target.dataset.page);
renderTable();
}
});
// Export functionality
exportBtn.addEventListener('click', () => {
exportModal.classList.add('active');
});
closeModal.addEventListener('click', () => {
exportModal.classList.remove('active');
});
exportModal.addEventListener('click', (e) => {
if (e.target === exportModal) {
exportModal.classList.remove('active');
}
});
confirmExport.addEventListener('click', () => {
const format = document.querySelector('input[name="exportFormat"]:checked').value;
const dataToExport = selectedRows.size > 0
? allData.filter(item => selectedRows.has(item.id))
: filteredData;
loading.classList.add('active');
setTimeout(() => {
let content = '';
let filename = `neural_implant_registry_${new Date().toISOString().split('T')[0]}`;
switch (format) {
case 'json':
content = JSON.stringify(dataToExport, null, 2);
filename += '.json';
break;
case 'csv':
const headers = ['Subject ID', 'Name', 'Implant Model', 'Installation Date', 'Status', 'Risk Level', 'Neural Sync'];
const rows = dataToExport.map(item =>
[item.id, item.name, item.model, item.date, item.status, item.risk, `${item.sync}%`].join(',')
);
content = [headers.join(','), ...rows].join('\n');
filename += '.csv';
break;
case 'xml':
content = '<?xml version="1.0" encoding="UTF-8"?>\n<neural_implants>\n';
dataToExport.forEach(item => {
content += ' <subject>\n';
content += ` <id>${item.id}</id>\n`;
content += ` <name>${item.name}</name>\n`;
content += ` <model>${item.model}</model>\n`;
content += ` <installation_date>${item.date}</installation_date>\n`;
content += ` <status>${item.status}</status>\n`;
content += ` <risk_level>${item.risk}</risk_level>\n`;
content += ` <neural_sync>${item.sync}</neural_sync>\n`;
content += ' </subject>\n';
});
content += '</neural_implants>';
filename += '.xml';
break;
}
// Create download
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
loading.classList.remove('active');
exportModal.classList.remove('active');
}, 1000);
});
// Initialize
renderTable();
// Add glitch effect on hover
document.querySelectorAll('.button').forEach(button => {
button.addEventListener('mouseenter', () => {
button.style.animation = 'glitch-1 0.3s infinite';
});
button.addEventListener('mouseleave', () => {
button.style.animation = '';
});
});
</script>
</body>
</html>