feat: entry templates + tag autocomplete + slideover push + robustness bundle
- Entry templates: new vault_entries.template column drives a typed
sub-kind ('credit-card', 'ssh-key', 'server', 'recovery-codes'). Card
+ table label off the template, badge reads "credit card" instead of
"note". Templates seed kind=note (no site/password required), use
custom_fields with optional dropdown options (brand, month/year,
protocol). Round-tripped across export/import/duplicate/master-pw
rotation, preserved by partial PUTs via a HasTemplate flag.
- Custom fields: support per-field `options[]` rendering as <select>
(card brand, expiry MM/YYYY, SSH/server protocol).
- Tags: existing-tag autocomplete dropdown under the chip input,
filtered against what's already selected.
- Search history: per-query X for individual delete + 1s debounced
commit (no Enter required).
- Slideover: clicking outside closes again (drag-selection respected
via mousedown origin tracker), Esc closes, X closes. App shell is
pushed left by 420px when the panel is open so the table / pagination
/ sort / search stay visible and interactive.
- Export/import: JSON now round-trips custom_fields, attachments
(decrypted to base64, re-encrypted under current key on restore),
icon_b64, and template. CSV warning lists what's not included.
- Auto-backup: same payload shape as user-driven export.
- Notes: import (JSON + CSV) accepts kind=note with empty site,
preserves title/template/custom_fields. CSV parser detects kind/
template columns.
- Bulk-import response returns `ids[]` parallel to input so the
client can map back to new entry IDs (drives attachment restore).
- Move-to-folder bugs fixed: moveEntryToFolder, batchMoveToFolder,
addTag, batchAddTag were all silently wiping TOTP / custom_fields
/ kind / template via partial PUT. Now re-ship full payload.
- Master-pw rotation: server mints a fresh session token + csrf so
the very next request after rotation no longer ESessionRejects.
Client adopts the new pair. Attachments are re-encrypted client-side
during rotation (GET old → decrypt with old key → encrypt with new
→ PUT). New endpoints: GET /attachments/all, PUT /attachments/:id.
- Duplicate: carries icon_b64 + template + attachments to the copy.
- HandleCreateEntry: accepts icon_b64.
- FireDAC param fix: all blob/icon/custom_fields params use ftMemo +
.Value assignment so SQLite TEXT no longer truncates to 4000 chars
(deepseek's 200+ KB favicon was being wiped on lock/unlock).
- HandleSetEntryIcon cap: 262144 → 524288 chars (base64 of a 256 KB
raw fetch overflows the old cap, fails silently in saveEntryIcon).
- Native save dialog: surfaces server errors instead of swallowing.
- Modals: reauth (export) + backup-password prompt support inline
error display, retry up to 5 attempts, then hard-stop.
- Keyboard cursor (j/k): bootstraps to current page, auto-paginates
when the cursor crosses a page boundary, Enter opens slideover.
- Slideover focuses Title on edit-open so j/k → Enter → type Just
Works.
- TOTP tool: Esc closes the modal.
- App version + launch mode (auto/manual): exposed via bridge,
surfaced in Settings → Account. Autostart launches suppress the
first-time tray balloon.
- Passkey button hidden (Delphi backend stubs WebAuthn at 501).
- TEST_PLAN.md captured for regression coverage.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+27
-2
@@ -45,6 +45,7 @@
|
||||
<symbol id="i-key" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 2-9.6 9.6"/><circle cx="7.5" cy="15.5" r="5.5"/><path d="m21 2-2 2 2 2-3 3-2-2"/></symbol>
|
||||
<symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></symbol>
|
||||
<symbol id="i-printer" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></symbol>
|
||||
<symbol id="i-pin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 17v5"/><path d="M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z"/></symbol>
|
||||
<symbol id="i-paperclip" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05 12.25 20.24a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></symbol>
|
||||
<symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></symbol>
|
||||
<symbol id="i-empty-vault" viewBox="0 0 120 120" fill="none">
|
||||
@@ -121,7 +122,7 @@
|
||||
<svg><use href="#i-unlock"/></svg>
|
||||
Unlock
|
||||
</button>
|
||||
<button id="passkeyBtn" type="button" class="btn btn-ghost btn-block">
|
||||
<button id="passkeyBtn" type="button" class="btn btn-ghost btn-block" style="display:none">
|
||||
<svg><use href="#i-lock"/></svg>
|
||||
Use a passkey
|
||||
</button>
|
||||
@@ -266,6 +267,7 @@
|
||||
<svg><use href="#i-search"/></svg>
|
||||
<input id="searchInput" type="search" placeholder="Search…" autocomplete="off">
|
||||
<kbd>Ctrl+K</kbd>
|
||||
<div id="searchHistoryMenu" class="search-history is-hidden"></div>
|
||||
</div>
|
||||
<div class="topbar-actions">
|
||||
<div class="view-toggle">
|
||||
@@ -311,6 +313,24 @@
|
||||
<svg><use href="#i-edit"/></svg>
|
||||
<span>New note</span>
|
||||
</button>
|
||||
<div class="dropdown-sep"></div>
|
||||
<div class="dropdown-section">Templates</div>
|
||||
<button class="dropdown-item" data-new-template="credit-card">
|
||||
<svg><use href="#i-key"/></svg>
|
||||
<span>Credit card</span>
|
||||
</button>
|
||||
<button class="dropdown-item" data-new-template="ssh-key">
|
||||
<svg><use href="#i-shield"/></svg>
|
||||
<span>SSH key</span>
|
||||
</button>
|
||||
<button class="dropdown-item" data-new-template="server">
|
||||
<svg><use href="#i-globe"/></svg>
|
||||
<span>Server credentials</span>
|
||||
</button>
|
||||
<button class="dropdown-item" data-new-template="recovery-codes">
|
||||
<svg><use href="#i-shield"/></svg>
|
||||
<span>Recovery codes (note)</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-menu">
|
||||
@@ -647,9 +667,12 @@
|
||||
|
||||
<div class="slideover-field">
|
||||
<div class="slideover-field-label">Account</div>
|
||||
<p style="font-size:12px;color:var(--text-dim);margin:0 0 8px">
|
||||
<p style="font-size:12px;color:var(--text-dim);margin:0 0 4px">
|
||||
Signed in as <b id="settingUser"></b>
|
||||
</p>
|
||||
<p style="font-size:11px;color:var(--text-faint);margin:0 0 8px" id="settingVersionRow">
|
||||
PMServer <span id="settingVersion">—</span>
|
||||
</p>
|
||||
<button class="btn btn-ghost btn-sm" id="changeMasterBtn">
|
||||
<svg><use href="#i-lock"/></svg> Change master password
|
||||
</button>
|
||||
@@ -711,6 +734,8 @@
|
||||
<span>Master password</span>
|
||||
<input id="reauthPassword" type="password" required autofocus>
|
||||
</label>
|
||||
<p id="reauthError" class="is-hidden"
|
||||
style="margin:8px 0 0;color:var(--danger);font-size:12px"></p>
|
||||
</form>
|
||||
<footer class="modal-footer">
|
||||
<button type="button" class="btn btn-ghost" data-close>Cancel</button>
|
||||
|
||||
Reference in New Issue
Block a user