/** * Settings page — syncs the settings form with the persisted settings object * and saves changes to the native host. * Depends on: core/utils.js ($), core/state.js (settings, SETTINGS_DEFAULTS), ui/toast.js (showToast) */ /** * True while the user has made unsaved changes to settings controls. * Prevents the 2-second refresh cycle from overwriting in-progress edits. */ let _settingsDirty = false; /** Mark settings as having unsaved changes. */ function _markSettingsDirty() { _settingsDirty = true; } /** Clear the dirty flag (called after a successful save or reset). */ function _clearSettingsDirty() { _settingsDirty = false; } /** * Sync all settings form controls with the current `settings` object. * Called on every refresh cycle to keep the UI in sync with persisted state. * Skipped while the user has unsaved changes to avoid reverting their edits. */ function updateSettingsUI() { if (_settingsDirty) return; $('toggleNotify')?.classList.toggle('active', settings.notifyOnDisconnect === true); $('toggleNotifyTunnelError')?.classList.toggle('active', settings.notifyOnTunnelError !== false); $('toggleTunnelAutoReconnect')?.classList.toggle('active', settings.tunnelAutoReconnect === true); $('toggleSyncServers')?.classList.toggle('active', settings.syncServers === true); $('toggleDebug')?.classList.toggle('active', settings.debug === true); $('toggleDisableFileUrls')?.classList.toggle('active', settings.disableOnFileUrls === true); $('toggleTheme')?.classList.toggle('active', document.documentElement.getAttribute('data-theme') === 'light'); $('toggleLatencyPing')?.classList.toggle('active', settings.latencyPingEnabled === true); const proxyPortEl = $('proxyPort'); const readyTimeoutMsEl = $('readyTimeoutMs'); const backupRetentionEl = $('backupRetention'); const backupIntervalEl = $('backupIntervalHours'); const latencyPingIntervalEl = $('latencyPingIntervalMs'); const dashboardRefreshEl = $('dashboardRefreshIntervalMs'); if (proxyPortEl) proxyPortEl.value = settings.proxyPort ?? SETTINGS_DEFAULTS.proxyPort; if (readyTimeoutMsEl) readyTimeoutMsEl.value = settings.readyTimeoutMs ?? SETTINGS_DEFAULTS.readyTimeoutMs; if (backupRetentionEl) backupRetentionEl.value = settings.backupRetention ?? SETTINGS_DEFAULTS.backupRetention; if (backupIntervalEl) backupIntervalEl.value = settings.backupIntervalHours ?? SETTINGS_DEFAULTS.backupIntervalHours; if (latencyPingIntervalEl) latencyPingIntervalEl.value = (settings.latencyPingIntervalMs ?? SETTINGS_DEFAULTS.latencyPingIntervalMs) / 1000; if (dashboardRefreshEl) dashboardRefreshEl.value = String(settings.dashboardRefreshIntervalMs ?? SETTINGS_DEFAULTS.dashboardRefreshIntervalMs ?? 2000); } function saveSettings() { settings.notifyOnDisconnect = $('toggleNotify')?.classList.contains('active') ?? SETTINGS_DEFAULTS.notifyOnDisconnect; settings.notifyOnTunnelError = $('toggleNotifyTunnelError')?.classList.contains('active') ?? SETTINGS_DEFAULTS.notifyOnTunnelError; settings.tunnelAutoReconnect = $('toggleTunnelAutoReconnect')?.classList.contains('active') ?? SETTINGS_DEFAULTS.tunnelAutoReconnect; settings.syncServers = $('toggleSyncServers')?.classList.contains('active') ?? SETTINGS_DEFAULTS.syncServers; settings.debug = $('toggleDebug')?.classList.contains('active') ?? SETTINGS_DEFAULTS.debug; settings.disableOnFileUrls = $('toggleDisableFileUrls')?.classList.contains('active') ?? SETTINGS_DEFAULTS.disableOnFileUrls; settings.latencyPingEnabled = $('toggleLatencyPing')?.classList.contains('active') ?? SETTINGS_DEFAULTS.latencyPingEnabled; settings.proxyPort = parseInt($('proxyPort')?.value, 10) || SETTINGS_DEFAULTS.proxyPort; settings.readyTimeoutMs = parseInt($('readyTimeoutMs')?.value, 10) || SETTINGS_DEFAULTS.readyTimeoutMs; settings.backupRetention = Math.max(1, parseInt($('backupRetention')?.value, 10) || SETTINGS_DEFAULTS.backupRetention); settings.backupIntervalHours = Math.max(0, parseInt($('backupIntervalHours')?.value, 10) || 0); const pingSecs = parseFloat($('latencyPingIntervalMs')?.value); settings.latencyPingIntervalMs = isNaN(pingSecs) || pingSecs <= 0 ? SETTINGS_DEFAULTS.latencyPingIntervalMs : Math.round(pingSecs * 1000); const refreshVal = parseInt($('dashboardRefreshIntervalMs')?.value, 10); settings.dashboardRefreshIntervalMs = ([0, 2000, 5000, 10000].includes(refreshVal) ? refreshVal : SETTINGS_DEFAULTS.dashboardRefreshIntervalMs); chrome.runtime.sendMessage( { target: 'holesail-native', action: 'send', payload: { type: 'updateSettings', payload: { ...settings } } }, (response) => { if (chrome.runtime.lastError) { showToast('Settings save failed: ' + chrome.runtime.lastError.message, 'error'); return; } if (response && response.ok) { if (response.settings) settings = { ...SETTINGS_DEFAULTS, ...response.settings }; _clearSettingsDirty(); // Restart the ping interval so the new interval / enabled state takes effect immediately if (typeof window._restartPingInterval === 'function') window._restartPingInterval(); if (typeof window._restartRefreshInterval === 'function') window._restartRefreshInterval(); if (response.requiresRestart) { showToast('Settings saved — restart the native host for proxy port changes to take effect', 'warning'); } else { showToast('Settings saved', 'success'); } } else { showToast(response?.error || 'Failed to save settings', 'error'); } } ); }