CI / Build & Test (push) Successful in 3m19s
- Add unit tests (hostname-validator, TLDs, payload-schemas) and integration tests for message handler registry - Refactor native host message router into handler registry (handlers/state, tunnels, ssh, rdp, backup, ca, connections) - Add ESLint config and npm test + lint steps in CI - Dashboard: visibility-based refresh pause, configurable refresh interval (2s/5s/10s/paused) - Accessibility: ARIA on nav and modals, focus trap and restore, prefers-reduced-motion - Empty states: primary action buttons for virtual hosts, servers, service tunnels - Native host rate limiting for backup and CA operations; update SECURITY.md - CONTRIBUTING: "Adding a new dashboard page", dev workflow; add npm run dev script
86 lines
2.9 KiB
JavaScript
86 lines
2.9 KiB
JavaScript
/**
|
|
* Dashboard entry point. Guards against loading outside the dashboard context,
|
|
* initialises navigation and all page event handlers, then starts the 2-second
|
|
* refresh cycle.
|
|
* Depends on: all other dashboard modules (loaded before this file)
|
|
*/
|
|
|
|
/**
|
|
* Guard: only run when loaded as the actual dashboard page.
|
|
*/
|
|
if (!document.getElementById('page-dashboard') && !document.querySelector('.sidebar-logo')) {
|
|
throw new Error('dashboard scripts loaded outside dashboard context — aborting');
|
|
}
|
|
|
|
/**
|
|
* Initialise the dashboard: set the version string, wire up navigation and all
|
|
* page event handlers, run the first state refresh, and start the 2-second polling interval.
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function init() {
|
|
log('Dashboard initializing…');
|
|
|
|
try {
|
|
const manifest = chrome.runtime.getManifest();
|
|
const versionEl = $('sidebarVersion');
|
|
if (versionEl && manifest.version) {
|
|
versionEl.textContent = 'v' + manifest.version + ' · hole.sail';
|
|
}
|
|
} catch (_) {}
|
|
|
|
setupNavigation();
|
|
setupEvents();
|
|
setupCertValidator();
|
|
setupSshEvents();
|
|
setupRdpEvents();
|
|
setupBackupEvents();
|
|
await refresh();
|
|
|
|
// Main state-refresh interval: configurable (2s / 5s / 10s), or paused when tab is hidden or setting is 0.
|
|
let _refreshIntervalId = null;
|
|
function _getRefreshMs() {
|
|
const ms = (settings.dashboardRefreshIntervalMs !== undefined ? settings.dashboardRefreshIntervalMs : 2000);
|
|
return (typeof ms === 'number' && ms >= 0 ? ms : 2000);
|
|
}
|
|
function _startRefreshInterval() {
|
|
if (_refreshIntervalId) clearInterval(_refreshIntervalId);
|
|
_refreshIntervalId = null;
|
|
if (document.visibilityState === 'hidden') return;
|
|
const ms = _getRefreshMs();
|
|
if (ms <= 0) return;
|
|
_refreshIntervalId = setInterval(refresh, ms);
|
|
}
|
|
function _restartRefreshInterval() {
|
|
_startRefreshInterval();
|
|
}
|
|
document.addEventListener('visibilitychange', () => {
|
|
if (document.visibilityState === 'visible') {
|
|
refresh();
|
|
_startRefreshInterval();
|
|
} else {
|
|
if (_refreshIntervalId) { clearInterval(_refreshIntervalId); _refreshIntervalId = null; }
|
|
}
|
|
});
|
|
_startRefreshInterval();
|
|
window._restartRefreshInterval = _restartRefreshInterval;
|
|
|
|
// Latency-ping interval — driven by settings.latencyPingIntervalMs.
|
|
let _pingInterval = null;
|
|
function _restartPingInterval() {
|
|
if (_pingInterval) clearInterval(_pingInterval);
|
|
_pingInterval = null;
|
|
if (settings.latencyPingEnabled === false) return;
|
|
const ms = (settings.latencyPingIntervalMs > 0 ? settings.latencyPingIntervalMs : 5000);
|
|
_pingInterval = setInterval(_pingLatencyBadges, ms);
|
|
}
|
|
_restartPingInterval();
|
|
window._restartPingInterval = _restartPingInterval;
|
|
|
|
window.addEventListener('beforeunload', () => {
|
|
if (_refreshIntervalId) clearInterval(_refreshIntervalId);
|
|
if (_pingInterval) clearInterval(_pingInterval);
|
|
}, { once: true });
|
|
}
|
|
|
|
init();
|