feat: PIN unlock + table column picker + edit-position chooser + UX
- PIN unlock: device-local 4-12 digit shortcut, DPAPI-wrapped vault
key. Three modes (state.unlockMode): pw / pin / pw+pin. PIN
derives a wrap key via PBKDF2(pin, salt, 100k) and unwraps the
stored vault key (mirrors the Quick Unlock blob shape).
Anti-brute-force: 5 wrong attempts wipes the blob. Setup gated by
master-pw reauth so an unattended unlocked laptop can't be
backdoored. Master pw rotation clears the PIN blob (key drift).
loadServerSettings post-sync demotes pin/both -> pw when the local
blob is missing, so a wiped device re-syncs the correct mode up.
New unit PM.PinUnlock.pas + cmd://pin/{store,get,clear,status}.
- Table column picker: ⚙ in topbar (table view only), checkbox menu
for Site/Username/Folder/Updated. Site also drives showSiteOnCards
so the existing "Show site / URL" toggle in Settings stays in
sync. NAME column auto-widths (180px min, content max, +32px
right padding) so column hugs the next one without truncating.
- Editor position chooser (Appearance setting): Slide-over right /
left / Centered modal. Scoped to #slideover + #settingsPanel so
the click-outside / pointer-events logic doesn't accidentally
trap the modal-style empty viewport.
- Confirm before discarding unsaved edits: state.confirmOnUnsaved
setting (default ON), prompts on X / Esc / click-outside / switch-
to-other-entry. Also gates Lock vault / Sign out actions when the
editor is dirty; auto-lock and system-lock paths bypass to avoid
blocking on an unattended machine.
- Open-in-browser button added to the actions cell of the table
view (was card-only).
- Entry templates pass folder customization + template id through
duplicate / export / import / auto-backup roundtrips.
- Folder color + icon now persisted across export/import: payload.
folders carries name/color/icon; import creates missing folders
additively (existing local customisation kept).
- Bulk move-to-folder, batch add-tag, single add-tag now re-ship
the full entry payload so partial PUTs don't silently wipe
TOTP / custom_fields / kind / template.
- FireDAC: switched ftString -> ftMemo for icon_b64 / custom_fields
/ TOTP / template params and replaced .AsString with .Value so a
large (~200 KB) DeepSeek favicon no longer gets truncated at the
default ANSI 4000-char cap.
- Unicode filenames: attachment INSERT now uses ftWideString +
.AsWideString so non-ANSI filenames round-trip instead of being
mangled to "?".
- HandleSetEntryIcon cap raised 256 KB -> 512 KB chars to accept
base64 data URIs produced by max-raw favicon fetches.
- promptDialog + askReauth support inline `error` line + retry-
with-count loops on doExport reauth and auto-backup password
setup (5 attempts cap before bailing).
- Recently used moved from Tools to Vault section in the sidebar.
- Auth screen passkey button hidden (Delphi backend stubs WebAuthn).
- Sensitive cmd://favicon/refresh-style buttons in Settings now
stopPropagation so the document-level "close panel" handler
doesn't dismiss Settings mid-async during DOM reparenting.
- TEST_PLAN.md: +PIN unlock section.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+68
-5
@@ -105,7 +105,7 @@
|
||||
<span>Username</span>
|
||||
<input id="loginUsername" type="text" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<label class="field" id="loginPasswordField">
|
||||
<span>Master password</span>
|
||||
<div class="input-with-action">
|
||||
<input id="loginPassword" type="password" required>
|
||||
@@ -114,6 +114,15 @@
|
||||
</button>
|
||||
</div>
|
||||
</label>
|
||||
<label class="field" id="loginPinField" style="display:none">
|
||||
<span>PIN</span>
|
||||
<input id="loginPin" type="password" inputmode="numeric"
|
||||
autocomplete="off" maxlength="12">
|
||||
</label>
|
||||
<button type="button" id="loginUseMasterBtn" class="btn btn-link btn-block"
|
||||
style="font-size:12px;margin-top:-4px;display:none">
|
||||
Use master password instead
|
||||
</button>
|
||||
<label class="remember-row">
|
||||
<input type="checkbox" id="loginRememberUser">
|
||||
<span>Remember username on this device</span>
|
||||
@@ -177,6 +186,10 @@
|
||||
<span>Notes</span>
|
||||
<span class="nav-count" id="countNotes">0</span>
|
||||
</button>
|
||||
<button class="nav-item" id="sidebarRecentBtn">
|
||||
<svg><use href="#i-rotate-ccw"/></svg>
|
||||
<span>Recently used</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-section" data-section="folders">
|
||||
@@ -224,10 +237,6 @@
|
||||
<svg><use href="#i-key"/></svg>
|
||||
<span>TOTP generator</span>
|
||||
</button>
|
||||
<button class="nav-item" id="sidebarRecentBtn">
|
||||
<svg><use href="#i-rotate-ccw"/></svg>
|
||||
<span>Recently used</span>
|
||||
</button>
|
||||
<button class="nav-item" id="sidebarHealthBtn">
|
||||
<svg><use href="#i-alert"/></svg>
|
||||
<span>Vault health</span>
|
||||
@@ -281,6 +290,12 @@
|
||||
<svg><use href="#i-table"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="cols-wrap" id="colsWrap" style="display:none;position:relative">
|
||||
<button class="icon-btn" id="colsBtn" title="Show / hide columns">
|
||||
<svg><use href="#i-settings"/></svg>
|
||||
</button>
|
||||
<div class="cols-menu is-hidden" id="colsMenu"></div>
|
||||
</div>
|
||||
<div class="filters-wrap">
|
||||
<button class="icon-btn" id="filtersBtn" title="Filters">
|
||||
<svg><use href="#i-list"/></svg>
|
||||
@@ -409,6 +424,27 @@
|
||||
<option value="created:asc">Oldest created</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<span>
|
||||
Editor position
|
||||
<small class="setting-hint">Where the add / edit panel and Settings appear. Affects both side panels.</small>
|
||||
</span>
|
||||
<select id="settingEditorPosition">
|
||||
<option value="right">Slide-over (right)</option>
|
||||
<option value="left">Slide-over (left)</option>
|
||||
<option value="center">Centered modal</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<span>
|
||||
Confirm before closing unsaved edits
|
||||
<small class="setting-hint">When the entry editor has changes, ask before dismissing it via X, Esc, or click-outside.</small>
|
||||
</span>
|
||||
<label class="toggle">
|
||||
<input type="checkbox" id="settingConfirmUnsaved">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<span>Compact action menu (⋯)</span>
|
||||
<label class="toggle">
|
||||
@@ -678,6 +714,33 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="slideover-field" id="pinUnlockField" style="display:none">
|
||||
<div class="slideover-field-label">PIN unlock</div>
|
||||
<p id="pinStatus" style="font-size:12px;color:var(--text-dim);margin:0 0 8px;line-height:1.5">
|
||||
No PIN set.
|
||||
</p>
|
||||
<p style="font-size:11px;color:var(--text-faint);margin:0 0 8px;line-height:1.4">
|
||||
Shortcut unlock with a 4–6 digit PIN. Stored DPAPI-encrypted on
|
||||
this device. <b>Master password is still required for sensitive
|
||||
actions</b> (export, change master password, recovery code…).
|
||||
</p>
|
||||
<div class="setting-row" style="margin:0 0 8px">
|
||||
<span>
|
||||
Unlock method
|
||||
<small class="setting-hint">Master pw (default), PIN only, or both required.</small>
|
||||
</span>
|
||||
<select id="settingUnlockMode">
|
||||
<option value="pw">Master password</option>
|
||||
<option value="pin">PIN only</option>
|
||||
<option value="both">Master password + PIN</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="display:flex;gap:6px;flex-wrap:wrap">
|
||||
<button class="btn btn-ghost btn-sm" id="pinSetBtn">Set PIN</button>
|
||||
<button class="btn btn-ghost btn-sm is-danger" id="pinRemoveBtn" style="display:none">Remove PIN</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="slideover-field">
|
||||
<div class="slideover-field-label">Quick unlock</div>
|
||||
<p id="quickUnlockStatus" style="font-size:12px;color:var(--text-dim);margin:0 0 8px;line-height:1.5">
|
||||
|
||||
Reference in New Issue
Block a user