feat(settings): keep backup pwd on disable, dedicated pwd button, tab icons

Auto-backup no longer wipes the stored password when disabled, so
re-enabling reuses it silently. A dedicated "Set/Change backup password"
button (mirrors sync) owns the password, with a warning status when
unset. Corrected the stale hint that claimed the backup pwd was derived
from the master password. Added icons to each settings tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-10 16:53:45 +01:00
parent 064ad14156
commit 45ba47f772
5 changed files with 44 additions and 20 deletions
+2
View File
@@ -2198,10 +2198,12 @@ body[data-editor-position="center"]:has(#settingsPanel.is-open)::before {
flex: 0 0 auto; border-right: 1px solid var(--border); overflow-y: auto; flex: 0 0 auto; border-right: 1px solid var(--border); overflow-y: auto;
} }
.settings-tab { .settings-tab {
display: flex; align-items: center; gap: 8px;
text-align: left; width: 100%; padding: 7px 12px; font-size: 12px; cursor: pointer; text-align: left; width: 100%; padding: 7px 12px; font-size: 12px; cursor: pointer;
color: var(--text-dim); background: transparent; border: 1px solid transparent; color: var(--text-dim); background: transparent; border: 1px solid transparent;
border-radius: 6px; white-space: nowrap; border-radius: 6px; white-space: nowrap;
} }
.settings-tab svg { width: 15px; height: 15px; flex: 0 0 auto; }
.settings-tab:hover { background: var(--bg-elev-2); } .settings-tab:hover { background: var(--bg-elev-2); }
.settings-tab.is-active { color: var(--accent-fg); background: var(--accent); border-color: var(--accent); } .settings-tab.is-active { color: var(--accent-fg); background: var(--accent); border-color: var(--accent); }
.slideover-field.is-tab-hidden { display: none; } .slideover-field.is-tab-hidden { display: none; }
Binary file not shown.
+12 -8
View File
@@ -415,11 +415,11 @@
</div> </div>
<div class="settings-main"> <div class="settings-main">
<div class="settings-tabs" id="settingsTabs"> <div class="settings-tabs" id="settingsTabs">
<button class="settings-tab is-active" data-tab="general">General</button> <button class="settings-tab is-active" data-tab="general"><svg><use href="#i-settings"/></svg>General</button>
<button class="settings-tab" data-tab="security">Security</button> <button class="settings-tab" data-tab="security"><svg><use href="#i-shield"/></svg>Security</button>
<button class="settings-tab" data-tab="autofill">Autofill</button> <button class="settings-tab" data-tab="autofill"><svg><use href="#i-key"/></svg>Autofill</button>
<button class="settings-tab" data-tab="account">Account</button> <button class="settings-tab" data-tab="account"><svg><use href="#i-user"/></svg>Account</button>
<button class="settings-tab" data-tab="sync">Sync &amp; Backup</button> <button class="settings-tab" data-tab="sync"><svg><use href="#i-rotate-ccw"/></svg>Sync &amp; Backup</button>
</div> </div>
<div class="slideover-body" id="settingsBody"> <div class="slideover-body" id="settingsBody">
<div class="slideover-field"> <div class="slideover-field">
@@ -769,10 +769,14 @@
</label> </label>
</div> </div>
<p style="font-size:11px;color:var(--text-faint);margin:0 0 8px;line-height:1.4"> <p style="font-size:11px;color:var(--text-faint);margin:0 0 8px;line-height:1.4">
Backups are encrypted with a password derived from your Backups are encrypted with a password you choose
master password (so restore needs the master at the (independent of your master password — you'll need it
time of backup). Filename pattern: <code>vault-autobackup-YYYYMMDD-HHmmss.json</code>. to restore). Filename pattern: <code>vault-autobackup-YYYYMMDD-HHmmss.json</code>.
</p> </p>
<div style="display:flex;gap:8px;align-items:center;margin-bottom:8px">
<button class="btn btn-ghost btn-sm" id="autoBackupSetPwdBtn">Set backup password</button>
<span id="autoBackupPwdStatus" style="font-size:11px;display:inline-flex;align-items:center;gap:4px"></span>
</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center"> <div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center">
<button class="btn btn-ghost btn-sm" id="autoBackupNowBtn"> <button class="btn btn-ghost btn-sm" id="autoBackupNowBtn">
<svg><use href="#i-log-out"/></svg> Backup now <svg><use href="#i-log-out"/></svg> Backup now
+29 -12
View File
@@ -59,6 +59,18 @@ function refreshAutoBackupUI(cfg) {
$('#autoBackupLast').textContent = cfg.last $('#autoBackupLast').textContent = cfg.last
? 'Last run: ' + cfg.last.replace('T', ' ').slice(0, 16) ? 'Last run: ' + cfg.last.replace('T', ' ').slice(0, 16)
: 'Never run yet'; : 'Never run yet';
$('#autoBackupSetPwdBtn').textContent = cfg.hasPwd
? 'Change backup password' : 'Set backup password';
const pwdStatus = $('#autoBackupPwdStatus');
if (cfg.hasPwd) {
pwdStatus.innerHTML = '';
pwdStatus.style.color = 'var(--text-faint)';
pwdStatus.textContent = 'Set.';
} else {
pwdStatus.style.color = 'var(--warning, #e0a800)';
pwdStatus.innerHTML = '<svg width="13" height="13"><use href="#i-alert"/></svg>' +
'<span>Not set — backups won\'t run</span>';
}
} }
async function pickAutoBackupFolder() { async function pickAutoBackupFolder() {
@@ -100,27 +112,32 @@ async function promptAndStoreBackupPwd() {
async function onToggleAutoBackup(ev) { async function onToggleAutoBackup(ev) {
const enabled = ev.target.checked; const enabled = ev.target.checked;
if (enabled) { if (enabled) {
const cfg = await loadAutoBackupConfig();
if (!cfg.hasPwd && !(await promptAndStoreBackupPwd())) {
ev.target.checked = false;
return;
}
Bridge.setPref(ABK.enabled, '1'); Bridge.setPref(ABK.enabled, '1');
$('#autoBackupConfig').style.display = ''; $('#autoBackupConfig').style.display = '';
toast('Auto-backup enabled'); // No pwd prompt here — the dedicated "Set backup password" button
// Refresh the dir/last display in case we came from cold state. // owns it (like sync). Backups no-op until it's set.
const fresh = await loadAutoBackupConfig(); const cfg = await loadAutoBackupConfig();
refreshAutoBackupUI(fresh); refreshAutoBackupUI(cfg);
toast(cfg.hasPwd ? 'Auto-backup enabled'
: 'Auto-backup enabled — set a backup password to start');
} else { } else {
Bridge.setPref(ABK.enabled, ''); Bridge.setPref(ABK.enabled, '');
// Forget the stored backup pwd so re-enabling prompts fresh — // Keep the stored pwd re-enabling reuses it silently. Change it
// gives the user a way to change it without extra UI. // anytime via the dedicated "Change backup password" button.
Bridge.setPref(ABK.pwd, '');
$('#autoBackupConfig').style.display = 'none'; $('#autoBackupConfig').style.display = 'none';
toast('Auto-backup disabled'); toast('Auto-backup disabled');
} }
} }
// Dedicated pwd button (mirrors sync's syncSetEncPwdFlow) so the user can
// change the backup password without the disable/re-enable dance.
async function changeBackupPwd() {
if (await promptAndStoreBackupPwd()) {
refreshAutoBackupUI(await loadAutoBackupConfig());
toast('Backup password saved');
}
}
async function runAutoBackupNow(silent) { async function runAutoBackupNow(silent) {
const cfg = await loadAutoBackupConfig(); const cfg = await loadAutoBackupConfig();
if (!cfg) return silent || toast('Bridge not available', 'error'); if (!cfg) return silent || toast('Bridge not available', 'error');
+1
View File
@@ -8994,6 +8994,7 @@ async function init() {
e.target.value = n; e.target.value = n;
Bridge.setPref(ABK.keep, String(n)); Bridge.setPref(ABK.keep, String(n));
}); });
$('#autoBackupSetPwdBtn').addEventListener('click', changeBackupPwd);
$('#autoBackupNowBtn').addEventListener('click', () => runAutoBackupNow()); $('#autoBackupNowBtn').addEventListener('click', () => runAutoBackupNow());
$('#settingFavicons').addEventListener('change', e => { $('#settingFavicons').addEventListener('change', e => {