0bf1a62897
Replace prototype dark theme with a professional light-theme design using Outfit (UI) and IBM Plex Mono (data) fonts, navy topbar, white card surfaces, and a full CSS variable system for colors, shadows, spacing, and radii. Add LED status indicators, panel components, and structured tile layout with header/meta/footer sections. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
119 lines
3.7 KiB
JavaScript
119 lines
3.7 KiB
JavaScript
(function() {
|
|
var dot = document.getElementById('sse-dot');
|
|
var es;
|
|
|
|
function connect() {
|
|
es = new EventSource('/events');
|
|
es.addEventListener('hello', function() {
|
|
dot.className = 'led led-green';
|
|
});
|
|
es.addEventListener('host.state_changed', function() {
|
|
window.location.reload();
|
|
});
|
|
es.onerror = function() {
|
|
dot.className = 'led led-red';
|
|
es.close();
|
|
setTimeout(connect, 3000);
|
|
};
|
|
}
|
|
|
|
connect();
|
|
})();
|
|
|
|
(function() {
|
|
var form = document.getElementById('upload-form');
|
|
if (!form) return;
|
|
|
|
var uploadId = 'upload-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
|
|
document.getElementById('upload-id').value = uploadId;
|
|
|
|
var progressDiv = document.getElementById('upload-progress');
|
|
var progressFill = document.getElementById('progress-fill');
|
|
var progressTitle = document.getElementById('progress-title');
|
|
var progressText = document.getElementById('progress-text');
|
|
var progressDetail = document.getElementById('progress-detail');
|
|
|
|
var uploadES = new EventSource('/events');
|
|
uploadES.addEventListener('image.upload_progress', function(e) {
|
|
var data;
|
|
try { data = JSON.parse(e.data); } catch(_) { return; }
|
|
if (data.upload_id !== uploadId) return;
|
|
|
|
progressText.textContent = data.detail;
|
|
if (data.stage === 'parsing' || data.stage === 'extracting') {
|
|
progressTitle.textContent = 'Extracting boot files...';
|
|
progressDetail.textContent = 'Processing ISO on server...';
|
|
}
|
|
if (data.stage === 'complete') {
|
|
progressFill.classList.add('complete');
|
|
progressTitle.textContent = 'Complete';
|
|
progressDetail.textContent = 'Redirecting...';
|
|
}
|
|
});
|
|
|
|
form.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
if (!form.checkValidity()) {
|
|
form.reportValidity();
|
|
return;
|
|
}
|
|
|
|
form.style.display = 'none';
|
|
progressDiv.style.display = 'block';
|
|
|
|
var formData = new FormData(form);
|
|
var xhr = new XMLHttpRequest();
|
|
|
|
xhr.upload.addEventListener('progress', function(ev) {
|
|
if (ev.lengthComputable) {
|
|
var pct = Math.round((ev.loaded / ev.total) * 100);
|
|
progressFill.style.width = pct + '%';
|
|
var mb = (ev.loaded / (1024 * 1024)).toFixed(1);
|
|
var totalMb = (ev.total / (1024 * 1024)).toFixed(1);
|
|
progressText.textContent = 'Uploading: ' + mb + ' / ' + totalMb + ' MB (' + pct + '%)';
|
|
}
|
|
});
|
|
|
|
xhr.upload.addEventListener('load', function() {
|
|
progressTitle.textContent = 'Processing ISO...';
|
|
progressText.textContent = 'Upload complete. Extracting kernel and initrd...';
|
|
progressDetail.textContent = 'This may take a minute...';
|
|
});
|
|
|
|
xhr.addEventListener('load', function() {
|
|
uploadES.close();
|
|
var resp;
|
|
try { resp = JSON.parse(xhr.responseText); } catch(_) { resp = {}; }
|
|
if (xhr.status >= 200 && xhr.status < 300 && resp.ok) {
|
|
window.location.href = '/images';
|
|
} else {
|
|
progressDiv.style.display = 'none';
|
|
form.style.display = 'block';
|
|
showUploadError(resp.error || 'Upload failed');
|
|
}
|
|
});
|
|
|
|
xhr.addEventListener('error', function() {
|
|
uploadES.close();
|
|
progressDiv.style.display = 'none';
|
|
form.style.display = 'block';
|
|
showUploadError('Network error during upload');
|
|
});
|
|
|
|
xhr.open('POST', '/images/upload');
|
|
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
|
xhr.send(formData);
|
|
});
|
|
|
|
function showUploadError(msg) {
|
|
var errDiv = form.parentNode.querySelector('.error');
|
|
if (!errDiv) {
|
|
errDiv = document.createElement('div');
|
|
errDiv.className = 'error';
|
|
form.parentNode.insertBefore(errDiv, form);
|
|
}
|
|
errDiv.textContent = msg;
|
|
}
|
|
})();
|