efactor(extension): modularize dashboard, background, and docs
CI / Build & Test (push) Failing after 28s
CI / Build & Test (push) Failing after 28s
Split monolithic dashboard.js (2753 lines) into 18 focused modules
under dashboard/{core,data,ui,pages}/ with refresh.js and events.js
as orchestrators. Extracted ~900-line inline <style> into dashboard.css
and moved dashboard.html to dashboard/dashboard.html.
Split background.js (609 lines) into background/{logs,state,proxy,
native-messaging,tab-lifecycle,message-router}.js with a thin entry
point using importScripts().
Deleted dead files: wrong-domain.js (duplicate of inline script).
Updated manifest.json web_accessible_resources for new paths.
Updated docs/ARCHITECTURE.md to reflect the new file structure.
No functionality changed. No build step introduced.
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
// Depends on: core/utils.js ($, escapeHtml), core/utils.js (timeAgo),
|
||||
// ui/toast.js (showToast), ui/modal.js (openModal, closeModal, showModalError)
|
||||
|
||||
let pendingRestoreFilename = null;
|
||||
let pendingDeleteFilename = null;
|
||||
|
||||
function formatBytes(bytes) {
|
||||
if (bytes < 1024) return bytes + ' B';
|
||||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
||||
return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
|
||||
}
|
||||
|
||||
function updateBackupsTable(backups) {
|
||||
const tbody = $('backupsTable');
|
||||
if (!tbody) return;
|
||||
const countEl = $('backupCount');
|
||||
if (countEl) countEl.textContent = backups ? backups.length : 0;
|
||||
if (!backups || backups.length === 0) {
|
||||
tbody.innerHTML = `<tr><td colspan="4"><div class="empty-state">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width:32px;height:32px;color:var(--text4)"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17,8 12,3 7,8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
|
||||
<div class="empty-state-title">No backups yet</div>
|
||||
<div class="empty-state-desc">Click "Take Backup" to create your first backup</div>
|
||||
</div></td></tr>`;
|
||||
return;
|
||||
}
|
||||
tbody.innerHTML = backups.map((b) => {
|
||||
const name = escapeHtml(b.filename);
|
||||
const created = b.createdAt ? timeAgo(b.createdAt) : '—';
|
||||
const size = b.size ? formatBytes(b.size) : '—';
|
||||
return `<tr>
|
||||
<td><span class="mono" style="font-size:12px;">${name}</span></td>
|
||||
<td>${created}</td>
|
||||
<td>${size}</td>
|
||||
<td>
|
||||
<div style="display:flex;gap:6px;">
|
||||
<button class="btn btn-secondary btn-sm" data-backup-restore="${escapeHtml(b.filename)}">Restore</button>
|
||||
<button class="btn btn-danger btn-sm" data-backup-delete="${escapeHtml(b.filename)}">Delete</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function refreshBackups() {
|
||||
chrome.runtime.sendMessage(
|
||||
{ target: 'holesail-native', action: 'send', payload: { type: 'listBackups' } },
|
||||
(response) => {
|
||||
if (chrome.runtime.lastError) return;
|
||||
if (response && response.ok) {
|
||||
updateBackupsTable(response.backups || []);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function setupBackupEvents() {
|
||||
$('btnTakeBackup')?.addEventListener('click', () => {
|
||||
const btn = $('btnTakeBackup');
|
||||
if (btn) { btn.disabled = true; btn.textContent = 'Creating…'; }
|
||||
chrome.runtime.sendMessage(
|
||||
{ target: 'holesail-native', action: 'send', payload: { type: 'createBackup' } },
|
||||
(response) => {
|
||||
if (btn) {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17,8 12,3 7,8"/><line x1="12" y1="3" x2="12" y2="15"/></svg> Take Backup`;
|
||||
}
|
||||
if (response && response.ok) {
|
||||
showToast('Backup created: ' + response.filename, 'success');
|
||||
refreshBackups();
|
||||
} else {
|
||||
showToast(response?.error || 'Backup failed', 'error');
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$('backupsTable')?.addEventListener('click', (e) => {
|
||||
const restoreBtn = e.target.closest('[data-backup-restore]');
|
||||
if (restoreBtn) {
|
||||
pendingRestoreFilename = restoreBtn.dataset.backupRestore;
|
||||
const nameEl = $('restoreBackupName');
|
||||
if (nameEl) nameEl.textContent = pendingRestoreFilename;
|
||||
openModal('modal-restoreBackup');
|
||||
return;
|
||||
}
|
||||
const deleteBtn = e.target.closest('[data-backup-delete]');
|
||||
if (deleteBtn) {
|
||||
pendingDeleteFilename = deleteBtn.dataset.backupDelete;
|
||||
const nameEl = $('deleteBackupName');
|
||||
if (nameEl) nameEl.textContent = pendingDeleteFilename;
|
||||
openModal('modal-deleteBackup');
|
||||
}
|
||||
});
|
||||
|
||||
$('restoreBackupConfirm')?.addEventListener('click', () => {
|
||||
if (!pendingRestoreFilename) return;
|
||||
const btn = $('restoreBackupConfirm');
|
||||
if (btn) btn.disabled = true;
|
||||
chrome.runtime.sendMessage(
|
||||
{ target: 'holesail-native', action: 'send', payload: { type: 'restoreBackup', payload: { filename: pendingRestoreFilename } } },
|
||||
(response) => {
|
||||
if (btn) btn.disabled = false;
|
||||
if (response && response.ok) {
|
||||
closeModal('modal-restoreBackup');
|
||||
const certsNote = response.restoredCerts ? ' Certificates restored.' : '';
|
||||
showToast('Backup restored.' + certsNote + ' Restart tunnels to apply changes.', 'success');
|
||||
pendingRestoreFilename = null;
|
||||
refresh();
|
||||
} else {
|
||||
showModalError('modal-restoreBackup', 'restoreBackupError', response?.error || 'Restore failed');
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$('deleteBackupConfirm')?.addEventListener('click', () => {
|
||||
if (!pendingDeleteFilename) return;
|
||||
const btn = $('deleteBackupConfirm');
|
||||
if (btn) btn.disabled = true;
|
||||
chrome.runtime.sendMessage(
|
||||
{ target: 'holesail-native', action: 'send', payload: { type: 'deleteBackup', payload: { filename: pendingDeleteFilename } } },
|
||||
(response) => {
|
||||
if (btn) btn.disabled = false;
|
||||
if (response && response.ok) {
|
||||
closeModal('modal-deleteBackup');
|
||||
showToast('Backup deleted', 'success');
|
||||
pendingDeleteFilename = null;
|
||||
refreshBackups();
|
||||
} else {
|
||||
showModalError('modal-deleteBackup', 'deleteBackupError', response?.error || 'Delete failed');
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user