d583a3f7d3
app.js 9363 -> 7962 lines. Three new classic-script modules: - app.attachments.js (250): blob crypto + upload/download UI, pure declarations, loads before app.js - app.autofill.js (276): Win32 combos, title->entry matching, picker, pure declarations, loads before app.js - app.unlock.js (907): Quick Unlock + PIN + recovery code grouped (same "enter without master pw" theme); assigns Bridge.onPinResult / onQuickUnlockResult at top level so it loads AFTER app.js, like app.sync.js Audit viewer stays in app.js (only 65 lines, not worth a file). Clipboard bridge helpers stay too (were interleaved in the quick-unlock section but unrelated). Registered in BuildAssets whitelist + index.html + APP_PARTS. Verified in-app: quick unlock cold-start, attachment upload/download. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1238 lines
72 KiB
HTML
1238 lines
72 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en" data-theme="dark">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Vault</title>
|
||
<link rel="stylesheet" href="css/style.css">
|
||
</head>
|
||
<body>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- ICON SPRITE (Lucide subset) -->
|
||
<!-- ============================================================ -->
|
||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
|
||
<symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></symbol>
|
||
<symbol id="i-unlock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 9.9-1"/></symbol>
|
||
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></symbol>
|
||
<symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5v14"/></symbol>
|
||
<symbol id="i-folder" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></symbol>
|
||
<symbol id="i-star" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></symbol>
|
||
<symbol id="i-trash" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></symbol>
|
||
<symbol id="i-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></symbol>
|
||
<symbol id="i-eye-off" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.88 9.88a3 3 0 1 0 4.24 4.24M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61M2 2l20 20"/></symbol>
|
||
<symbol id="i-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></symbol>
|
||
<symbol id="i-edit" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></symbol>
|
||
<symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></symbol>
|
||
<symbol id="i-settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z"/></symbol>
|
||
<symbol id="i-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></symbol>
|
||
<symbol id="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79Z"/></symbol>
|
||
<symbol id="i-globe" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10Z"/></symbol>
|
||
<symbol id="i-dice" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8" cy="8" r="1.2" fill="currentColor"/><circle cx="16" cy="8" r="1.2" fill="currentColor"/><circle cx="12" cy="12" r="1.2" fill="currentColor"/><circle cx="8" cy="16" r="1.2" fill="currentColor"/><circle cx="16" cy="16" r="1.2" fill="currentColor"/></symbol>
|
||
<symbol id="i-tag" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.59 13.41 13.42 20.58a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82Z"/><line x1="7" y1="7" x2="7.01" y2="7"/></symbol>
|
||
<symbol id="i-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></symbol>
|
||
<symbol id="i-log-in" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" y1="12" x2="3" y2="12"/></symbol>
|
||
<symbol id="i-rotate-ccw" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/></symbol>
|
||
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></symbol>
|
||
<symbol id="i-command" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 3a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3H6a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3V6a3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3h12a3 3 0 0 0 3-3 3 3 0 0 0-3-3Z"/></symbol>
|
||
<symbol id="i-log-out" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9"/></symbol>
|
||
<symbol id="i-grid" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></symbol>
|
||
<symbol id="i-list" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></symbol>
|
||
<symbol id="i-table" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/></symbol>
|
||
<symbol id="i-more" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="5" r="1.4" fill="currentColor"/><circle cx="12" cy="12" r="1.4" fill="currentColor"/><circle cx="12" cy="19" r="1.4" fill="currentColor"/></symbol>
|
||
<symbol id="i-chevron-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></symbol>
|
||
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol>
|
||
<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">
|
||
<circle cx="60" cy="60" r="50" fill="var(--accent-soft)"/>
|
||
<rect x="40" y="50" width="40" height="35" rx="4" fill="var(--bg-elev)" stroke="var(--accent)" stroke-width="2"/>
|
||
<path d="M48 50V40a12 12 0 0 1 24 0v10" fill="none" stroke="var(--accent)" stroke-width="2" stroke-linecap="round"/>
|
||
<circle cx="60" cy="65" r="3" fill="var(--accent)"/>
|
||
<line x1="60" y1="68" x2="60" y2="74" stroke="var(--accent)" stroke-width="2" stroke-linecap="round"/>
|
||
</symbol>
|
||
<symbol id="i-empty-trash" viewBox="0 0 120 120" fill="none">
|
||
<circle cx="60" cy="60" r="50" fill="var(--accent-soft)"/>
|
||
<path d="M40 45h40M48 45v-5a3 3 0 0 1 3-3h18a3 3 0 0 1 3 3v5M44 45v35a4 4 0 0 0 4 4h24a4 4 0 0 0 4-4V45" stroke="var(--accent)" stroke-width="2" stroke-linecap="round" fill="none"/>
|
||
<path d="M54 55v22M66 55v22" stroke="var(--accent)" stroke-width="2" stroke-linecap="round"/>
|
||
</symbol>
|
||
<symbol id="i-empty-search" viewBox="0 0 120 120" fill="none">
|
||
<circle cx="60" cy="60" r="50" fill="var(--accent-soft)"/>
|
||
<circle cx="55" cy="55" r="14" stroke="var(--accent)" stroke-width="2.5" fill="none"/>
|
||
<path d="m66 66 11 11" stroke="var(--accent)" stroke-width="2.5" stroke-linecap="round"/>
|
||
</symbol>
|
||
</svg>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- TOAST CONTAINER -->
|
||
<!-- ============================================================ -->
|
||
<div id="toastContainer" class="toast-container"></div>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- IDLE WARNING (auto-lock countdown) -->
|
||
<!-- ============================================================ -->
|
||
<div id="idleWarning" class="idle-warning is-hidden">
|
||
<svg class="idle-icon"><use href="#i-lock"/></svg>
|
||
<h3>Auto-lock</h3>
|
||
<p>Vault locks in <b id="idleCountdown">30</b>s due to inactivity</p>
|
||
<button class="btn btn-primary btn-block" id="idleStayBtn">Stay unlocked</button>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- AUTH SCREEN (login + register) -->
|
||
<!-- ============================================================ -->
|
||
<section id="authScreen" class="auth-screen">
|
||
<div class="auth-card">
|
||
<div class="auth-brand">
|
||
<svg class="auth-brand-icon"><use href="#i-lock"/></svg>
|
||
<h1>Vault</h1>
|
||
<p>Your passwords, encrypted locally.</p>
|
||
</div>
|
||
|
||
<div class="auth-tabs">
|
||
<button class="auth-tab is-active" data-tab="login">Login</button>
|
||
<button class="auth-tab" data-tab="register">Register</button>
|
||
</div>
|
||
|
||
<div id="authHint" class="auth-hint"></div>
|
||
|
||
<form id="loginForm" class="auth-form" autocomplete="off">
|
||
<label class="field">
|
||
<span>Username</span>
|
||
<input id="loginUsername" type="text" required>
|
||
</label>
|
||
<label class="field" id="loginPasswordField">
|
||
<span>Master password</span>
|
||
<div class="input-with-action">
|
||
<input id="loginPassword" type="password" required>
|
||
<button type="button" class="icon-btn" id="loginPwToggle" title="Show/hide password">
|
||
<svg><use href="#i-eye"/></svg>
|
||
</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>
|
||
</label>
|
||
<button id="loginBtn" type="submit" class="btn btn-primary btn-block">
|
||
<svg><use href="#i-unlock"/></svg>
|
||
Unlock
|
||
</button>
|
||
<button id="recoveryBtn" type="button" class="btn btn-link btn-block" style="font-size:12px;margin-top:4px">
|
||
Forgot master password? Use a recovery code
|
||
</button>
|
||
</form>
|
||
|
||
<form id="registerForm" class="auth-form is-hidden" autocomplete="off">
|
||
<label class="field">
|
||
<span>Username <span class="hint">min 3 chars</span></span>
|
||
<input id="regUsername" type="text" required minlength="3">
|
||
</label>
|
||
<label class="field">
|
||
<span>Master password <span class="hint">min 8 chars</span></span>
|
||
<input id="regPassword" type="password" required minlength="8">
|
||
<div class="strength-bar" id="regStrengthBar"></div>
|
||
</label>
|
||
<button id="registerBtn" type="submit" class="btn btn-primary btn-block">
|
||
<svg><use href="#i-plus"/></svg>
|
||
Create vault
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- APP SHELL (sidebar + main + slide-over) -->
|
||
<!-- ============================================================ -->
|
||
<div id="appShell" class="app-shell is-hidden">
|
||
|
||
<!-- ---- SIDEBAR ----------------------------------------- -->
|
||
<aside class="sidebar">
|
||
<div class="sidebar-brand">
|
||
<svg><use href="#i-lock"/></svg>
|
||
<span>Vault</span>
|
||
</div>
|
||
|
||
<nav class="sidebar-nav">
|
||
<button class="nav-item is-active" data-view="all">
|
||
<svg><use href="#i-globe"/></svg>
|
||
<span>All items</span>
|
||
<span class="nav-count" id="countAll">0</span>
|
||
</button>
|
||
<button class="nav-item" data-view="favorites">
|
||
<svg><use href="#i-star"/></svg>
|
||
<span>Favorites</span>
|
||
<span class="nav-count" id="countFav">0</span>
|
||
</button>
|
||
<button class="nav-item" data-view="notes">
|
||
<svg><use href="#i-edit"/></svg>
|
||
<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">
|
||
<div class="sidebar-section-header">
|
||
<button class="section-toggle" data-section-toggle="folders">
|
||
<svg class="section-chevron"><use href="#i-chevron-down"/></svg>
|
||
<span>Folders</span>
|
||
<span class="section-count" id="countFolders">0</span>
|
||
</button>
|
||
<button class="icon-btn icon-btn-sm" id="addFolderBtn" title="New folder">
|
||
<svg><use href="#i-plus"/></svg>
|
||
</button>
|
||
</div>
|
||
<nav class="sidebar-nav section-body" id="foldersList"></nav>
|
||
</div>
|
||
|
||
<div class="sidebar-section" data-section="tags">
|
||
<div class="sidebar-section-header">
|
||
<button class="section-toggle" data-section-toggle="tags">
|
||
<svg class="section-chevron"><use href="#i-chevron-down"/></svg>
|
||
<span>Tags</span>
|
||
<span class="section-count" id="countTags">0</span>
|
||
</button>
|
||
</div>
|
||
<nav class="sidebar-nav section-body" id="tagsList"></nav>
|
||
</div>
|
||
|
||
<div class="sidebar-section" data-section="tools">
|
||
<div class="sidebar-section-header">
|
||
<button class="section-toggle" data-section-toggle="tools">
|
||
<svg class="section-chevron"><use href="#i-chevron-down"/></svg>
|
||
<span>Tools</span>
|
||
</button>
|
||
</div>
|
||
<nav class="sidebar-nav section-body">
|
||
<button class="nav-item" id="sidebarGenBtn">
|
||
<svg><use href="#i-dice"/></svg>
|
||
<span>Generator</span>
|
||
</button>
|
||
<button class="nav-item" id="sidebarAuthenticatorBtn">
|
||
<svg><use href="#i-shield"/></svg>
|
||
<span>Authenticator</span>
|
||
</button>
|
||
<button class="nav-item" id="sidebarTotpToolBtn">
|
||
<svg><use href="#i-key"/></svg>
|
||
<span>TOTP generator</span>
|
||
</button>
|
||
<button class="nav-item" id="sidebarHealthBtn">
|
||
<svg><use href="#i-alert"/></svg>
|
||
<span>Vault health</span>
|
||
</button>
|
||
<button class="nav-item" id="sidebarAuditBtn">
|
||
<svg><use href="#i-list"/></svg>
|
||
<span>Audit log</span>
|
||
</button>
|
||
<button class="nav-item" id="sidebarImportBtn">
|
||
<svg><use href="#i-log-in"/></svg>
|
||
<span>Import vault</span>
|
||
</button>
|
||
<button class="nav-item" id="sidebarExportBtn">
|
||
<svg><use href="#i-log-out"/></svg>
|
||
<span>Export vault</span>
|
||
</button>
|
||
</nav>
|
||
</div>
|
||
|
||
<div class="sidebar-bottom">
|
||
<button class="nav-item" data-view="trash">
|
||
<svg><use href="#i-trash"/></svg>
|
||
<span>Trash</span>
|
||
<span class="nav-count" id="countTrash">0</span>
|
||
</button>
|
||
<button class="nav-item" id="settingsBtn">
|
||
<svg><use href="#i-settings"/></svg>
|
||
<span>Settings</span>
|
||
</button>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- ---- MAIN -------------------------------------------- -->
|
||
<main class="main">
|
||
<header class="topbar">
|
||
<div class="search">
|
||
<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">
|
||
<button class="view-btn is-active" id="viewCardsBtn" data-view="cards" title="Cards view">
|
||
<svg><use href="#i-grid"/></svg>
|
||
</button>
|
||
<button class="view-btn" id="viewListBtn" data-view="list" title="List view">
|
||
<svg><use href="#i-list"/></svg>
|
||
</button>
|
||
<button class="view-btn" id="viewTableBtn" data-view="table" title="Table view">
|
||
<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>
|
||
<span class="filters-count" id="filtersCount" style="display:none">0</span>
|
||
</button>
|
||
<div class="filters-menu is-hidden" id="filtersMenu"></div>
|
||
</div>
|
||
<button class="icon-btn" id="cheatsheetBtn" title="Keyboard shortcuts (?)">
|
||
<span style="font-weight:700;font-size:14px">?</span>
|
||
</button>
|
||
<button class="icon-btn" id="themeBtn" title="Toggle theme">
|
||
<svg class="theme-icon theme-icon-dark"><use href="#i-sun"/></svg>
|
||
<svg class="theme-icon theme-icon-light"><use href="#i-moon"/></svg>
|
||
</button>
|
||
<div class="new-entry-wrap">
|
||
<button class="btn btn-primary btn-sm" id="newEntryBtn">
|
||
<svg><use href="#i-plus"/></svg>
|
||
New
|
||
</button>
|
||
<button class="btn btn-primary btn-sm new-entry-caret" id="newEntryCaretBtn"
|
||
title="Choose entry type">
|
||
<svg><use href="#i-chevron-down"/></svg>
|
||
</button>
|
||
<div class="new-entry-menu is-hidden" id="newEntryMenu">
|
||
<button class="dropdown-item" data-new-kind="login">
|
||
<svg><use href="#i-key"/></svg>
|
||
<span>New login</span>
|
||
</button>
|
||
<button class="dropdown-item" data-new-kind="note">
|
||
<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">
|
||
<button class="user-chip" id="userChip">
|
||
<span class="user-avatar" id="userAvatar"></span>
|
||
<span id="userName">user</span>
|
||
</button>
|
||
<div class="user-dropdown is-hidden" id="userDropdown">
|
||
<button class="dropdown-item" id="dropdownSettingsBtn">
|
||
<svg><use href="#i-settings"/></svg> Settings
|
||
</button>
|
||
<button class="dropdown-item" id="lockBtn">
|
||
<svg><use href="#i-lock"/></svg> Lock vault
|
||
</button>
|
||
<button class="dropdown-item" id="logoutBtn">
|
||
<svg><use href="#i-log-out"/></svg> Sign out
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="content">
|
||
<div class="content-header">
|
||
<h2 id="contentTitle">All items</h2>
|
||
<span class="content-meta" id="contentMeta">0 items</span>
|
||
</div>
|
||
|
||
<!-- Active filter chips (populated by renderFilterChips) -->
|
||
<div id="filterChips" class="filter-chips is-hidden"></div>
|
||
|
||
<!-- Entry grid -->
|
||
<div id="entryGrid" class="entry-grid"></div>
|
||
|
||
<!-- Empty state — illustration + message are filled dynamically -->
|
||
<div id="emptyState" class="empty-state is-hidden">
|
||
<svg class="empty-illustration" id="emptyIllustration"><use href="#i-empty-vault"/></svg>
|
||
<h3 id="emptyTitle">Nothing here yet</h3>
|
||
<p id="emptyMessage">Click <b>+ New</b> to add your first password.</p>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<!-- ---- SLIDE-OVER DETAIL ------------------------------- -->
|
||
<aside class="slideover" id="slideover">
|
||
<header class="slideover-header">
|
||
<h3 id="slideoverTitle">Entry</h3>
|
||
<button class="icon-btn" id="slideoverClose"><svg><use href="#i-x"/></svg></button>
|
||
</header>
|
||
<div class="slideover-body" id="slideoverBody"></div>
|
||
</aside>
|
||
|
||
<!-- ---- SETTINGS SLIDE-OVER ----------------------------- -->
|
||
<aside class="slideover" id="settingsPanel">
|
||
<header class="slideover-header">
|
||
<h3>Settings</h3>
|
||
<button class="icon-btn" id="settingsClose"><svg><use href="#i-x"/></svg></button>
|
||
</header>
|
||
<div class="settings-search-wrap">
|
||
<svg class="settings-search-icon"><use href="#i-search"/></svg>
|
||
<input type="text" id="settingsSearch" class="settings-search-input"
|
||
placeholder="Search settings… (Esc to clear)" />
|
||
<button class="settings-search-clear" id="settingsSearchClear" type="button"
|
||
title="Clear search">
|
||
<svg><use href="#i-x"/></svg>
|
||
</button>
|
||
</div>
|
||
<div class="settings-main">
|
||
<div class="settings-tabs" id="settingsTabs">
|
||
<button class="settings-tab is-active" data-tab="general"><svg><use href="#i-settings"/></svg>General</button>
|
||
<button class="settings-tab" data-tab="security"><svg><use href="#i-shield"/></svg>Security</button>
|
||
<button class="settings-tab" data-tab="autofill"><svg><use href="#i-key"/></svg>Autofill</button>
|
||
<button class="settings-tab" data-tab="account"><svg><use href="#i-user"/></svg>Account</button>
|
||
<button class="settings-tab" data-tab="sync"><svg><use href="#i-rotate-ccw"/></svg>Sync & Backup</button>
|
||
</div>
|
||
<div class="slideover-body" id="settingsBody">
|
||
<div class="slideover-field">
|
||
<div class="slideover-field-label">Getting started</div>
|
||
<p style="font-size:12px;color:var(--text-dim);margin:0 0 8px;line-height:1.5">
|
||
A quick guided tour of the main features — search, autofill,
|
||
tools and sync.
|
||
</p>
|
||
<button class="btn btn-ghost btn-sm" id="startTourBtn">
|
||
<svg><use href="#i-log-in"/></svg> How it works
|
||
</button>
|
||
</div>
|
||
<div class="slideover-field">
|
||
<div class="slideover-field-label">Appearance</div>
|
||
<div class="setting-row">
|
||
<span>Theme</span>
|
||
<select id="settingTheme">
|
||
<option value="dark">Dark</option>
|
||
<option value="light">Light</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-row">
|
||
<span>Sort entries by</span>
|
||
<select id="settingSort">
|
||
<option value="name:asc">Name (A → Z)</option>
|
||
<option value="name:desc">Name (Z → A)</option>
|
||
<option value="updated:desc">Most recent first</option>
|
||
<option value="updated:asc">Oldest update first</option>
|
||
<option value="created:desc">Recently created</option>
|
||
<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">
|
||
<input type="checkbox" id="settingCompact">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-row">
|
||
<span>Mask usernames (ab***)</span>
|
||
<label class="toggle">
|
||
<input type="checkbox" id="settingMaskUser">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-row">
|
||
<span>
|
||
Show site / URL
|
||
<small class="setting-hint">On cards: shown under the display name when the entry has a custom title. In table view: adds a Site column.</small>
|
||
</span>
|
||
<label class="toggle">
|
||
<input type="checkbox" id="settingShowSite">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-row" id="settingFaviconsRow">
|
||
<span>
|
||
Fetch website icons
|
||
<small class="setting-hint">
|
||
Loads a small icon for each entry via DuckDuckGo
|
||
(privacy-friendly proxy). Each entry's domain is
|
||
sent to <b>icons.duckduckgo.com</b> once and cached
|
||
locally. OFF by default.
|
||
</small>
|
||
</span>
|
||
<label class="toggle">
|
||
<input type="checkbox" id="settingFavicons">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
<div id="settingFaviconActionsRow" style="display:flex;gap:6px;margin-top:6px">
|
||
<button class="btn btn-ghost btn-sm" id="settingFaviconsRefresh">
|
||
<svg><use href="#i-rotate-ccw"/></svg> Fetch missing
|
||
</button>
|
||
<button class="btn btn-ghost btn-sm" id="settingFaviconsRefreshAll">
|
||
Re-fetch all
|
||
</button>
|
||
<button class="btn btn-ghost btn-sm is-danger" id="settingFaviconsClear">
|
||
<svg><use href="#i-trash"/></svg> Clear cache
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="slideover-field">
|
||
<div class="slideover-field-label">Startup & tray</div>
|
||
<div class="setting-row" id="settingTrayNotifRow">
|
||
<span>
|
||
Show tray notifications
|
||
<small class="setting-hint">
|
||
The first time the app minimises to the tray
|
||
it shows a small balloon explaining how to
|
||
restore it. Turn off if you've seen it.
|
||
</small>
|
||
</span>
|
||
<label class="toggle">
|
||
<input type="checkbox" id="settingTrayNotif">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-row" id="settingAutoStartRow">
|
||
<span>
|
||
Start with Windows
|
||
<small class="setting-hint">
|
||
Launch this app in the tray when you sign in to
|
||
Windows — so the autofill hotkeys are ready
|
||
immediately. Device-only setting (per Windows user).
|
||
</small>
|
||
</span>
|
||
<label class="toggle">
|
||
<input type="checkbox" id="settingAutoStart">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="slideover-field">
|
||
<div class="slideover-field-label">Security</div>
|
||
<div class="setting-row">
|
||
<span>Auto-lock after (minutes)</span>
|
||
<select id="settingAutoLock">
|
||
<option value="0">Never</option>
|
||
<option value="1">1</option>
|
||
<option value="5">5</option>
|
||
<option value="15">15</option>
|
||
<option value="30">30</option>
|
||
<option value="60">60</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-row">
|
||
<span>Ask before moving to trash</span>
|
||
<label class="toggle">
|
||
<input type="checkbox" id="settingAskDelete">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
<div class="setting-row">
|
||
<span>
|
||
Auto-purge trash after
|
||
<small class="setting-hint">
|
||
Permanently delete entries that have been in
|
||
the trash for longer than this. Runs at every
|
||
unlock.
|
||
</small>
|
||
</span>
|
||
<select id="settingTrashPurge">
|
||
<option value="0">Never</option>
|
||
<option value="7">7 days</option>
|
||
<option value="30">30 days</option>
|
||
<option value="90">90 days</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-row">
|
||
<span>
|
||
Flag aged passwords
|
||
<small class="setting-hint">Show an "Aged" badge on entries whose password hasn't changed in this many days.</small>
|
||
</span>
|
||
<select id="settingPasswordExpiry">
|
||
<option value="0">Off</option>
|
||
<option value="90">90 days</option>
|
||
<option value="180">180 days</option>
|
||
<option value="365">1 year</option>
|
||
<option value="730">2 years</option>
|
||
</select>
|
||
</div>
|
||
<div class="setting-row">
|
||
<span>
|
||
Check passwords against breach database (HIBP)
|
||
<small class="setting-hint">Only the first 5 chars of the SHA-1 are sent — your passwords never leave the machine.</small>
|
||
</span>
|
||
<label class="toggle">
|
||
<input type="checkbox" id="settingHIBP">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="slideover-field">
|
||
<div class="slideover-field-label">Autofill</div>
|
||
<div class="setting-row" id="settingAutofillRow">
|
||
<span>
|
||
Autofill with global hotkey
|
||
<small class="setting-hint">
|
||
Press your hotkey in any browser to fill the matching
|
||
login. The vault must be unlocked.
|
||
</small>
|
||
</span>
|
||
<label class="toggle">
|
||
<input type="checkbox" id="settingAutofill">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
<div id="settingAutofillHotkeysRow" style="margin-top:8px">
|
||
<div class="setting-row">
|
||
<span style="font-size:12px;color:var(--text-dim)">
|
||
Full fill (username + Tab + password)
|
||
</span>
|
||
<button class="btn btn-ghost btn-sm hotkey-capture-btn" id="settingAutofillFullCombo">Ctrl+Shift+L</button>
|
||
</div>
|
||
<div class="setting-row">
|
||
<span style="font-size:12px;color:var(--text-dim)">
|
||
Password only (for step-2 forms / unlock screens)
|
||
</span>
|
||
<button class="btn btn-ghost btn-sm hotkey-capture-btn" id="settingAutofillPwdCombo">Ctrl+Shift+P</button>
|
||
</div>
|
||
<div class="setting-row">
|
||
<span style="font-size:12px;color:var(--text-dim)">
|
||
Quick search picker (find entry, fill from anywhere)
|
||
</span>
|
||
<button class="btn btn-ghost btn-sm hotkey-capture-btn" id="settingQuickSearchCombo">Ctrl+Shift+Q</button>
|
||
</div>
|
||
<p style="font-size:11px;color:var(--text-faint);margin:6px 0 0;line-height:1.4">
|
||
Click a button, then press your new combo. Needs Ctrl, Alt or Win + a letter / digit / F-key.
|
||
</p>
|
||
<button class="btn btn-ghost btn-sm" id="settingAutofillResetHotkeys" style="margin-top:6px">
|
||
Reset to defaults
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="slideover-field">
|
||
<div class="slideover-field-label">Clipboard privacy</div>
|
||
<p style="font-size:12px;color:var(--text-dim);margin:0 0 8px;line-height:1.5">
|
||
Passwords copied from this app are <b>excluded from Windows
|
||
clipboard history (Win+V)</b> and cloud clipboard, and cleared
|
||
automatically when the app is minimised — no manual setup needed.
|
||
</p>
|
||
<button class="btn btn-ghost btn-sm" id="openClipboardSettings">How?</button>
|
||
</div>
|
||
|
||
<div class="slideover-field">
|
||
<div class="slideover-field-label">Export</div>
|
||
<p style="font-size:12px;color:var(--text-dim);margin:0 0 8px;line-height:1.5">
|
||
Download an <b>encrypted</b> backup of your vault. You'll
|
||
choose a password independent of your master password —
|
||
save it carefully, you need it to restore.
|
||
</p>
|
||
<div style="display:flex;gap:6px;flex-wrap:wrap">
|
||
<button class="btn btn-ghost btn-sm" id="exportBtn">
|
||
<svg><use href="#i-log-out"/></svg> Encrypted JSON
|
||
</button>
|
||
<button class="btn btn-ghost btn-sm is-danger" id="exportCsvBtn"
|
||
title="Plaintext! Use only for migration">
|
||
<svg><use href="#i-alert"/></svg> Plaintext CSV
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="slideover-field">
|
||
<div class="slideover-field-label">Import</div>
|
||
<p style="font-size:12px;color:var(--text-dim);margin:0 0 8px;line-height:1.5">
|
||
Import a vault from a JSON export (this app) or a CSV file
|
||
(Bitwarden, KeePass, Chrome, 1Password…). Entries are added
|
||
to your current vault — duplicates are NOT removed.
|
||
</p>
|
||
<button class="btn btn-ghost btn-sm" id="importBtn">
|
||
<svg><use href="#i-log-in"/></svg> Import vault
|
||
</button>
|
||
</div>
|
||
|
||
<div class="slideover-field" id="syncField" style="display:none">
|
||
<div class="slideover-field-label">Sync (WebDAV)</div>
|
||
<p style="font-size:11px;color:var(--text-faint);margin:0 0 8px;line-height:1.5">
|
||
Bidirectional sync with a WebDAV server (Nextcloud,
|
||
ownCloud, Apache mod_dav). Auto-merge by entry
|
||
timestamp. <b>Master password is never sent to the
|
||
remote</b> — the snapshot is encrypted with a separate
|
||
sync password you set once per device (same on each).
|
||
</p>
|
||
<label style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
||
<input type="checkbox" id="settingSyncEnabled">
|
||
<span>Enable sync</span>
|
||
</label>
|
||
<div id="syncConfig" style="display:none;border-left:2px solid var(--border);padding-left:10px;margin-left:4px">
|
||
<div style="margin-bottom:6px">
|
||
<div style="font-size:11px;color:var(--text-faint);margin-bottom:4px">WebDAV URL (full path to the snapshot file)</div>
|
||
<input type="text" id="syncUrl" class="so-input"
|
||
placeholder="https://cloud.example.com/remote.php/dav/files/USER/PMServer/vault-sync.json"
|
||
style="width:100%;font-size:12px">
|
||
</div>
|
||
<div style="display:flex;gap:8px;margin-bottom:6px">
|
||
<label style="flex:1">
|
||
<div style="font-size:11px;color:var(--text-faint);margin-bottom:4px">Username</div>
|
||
<input type="text" id="syncUser" class="so-input" style="width:100%">
|
||
</label>
|
||
<label style="flex:1">
|
||
<div style="font-size:11px;color:var(--text-faint);margin-bottom:4px">Password / app token</div>
|
||
<input type="password" id="syncPwd" class="so-input" style="width:100%">
|
||
</label>
|
||
</div>
|
||
<div style="margin-bottom:8px">
|
||
<button class="btn btn-ghost btn-sm" id="syncTestBtn">Test connection</button>
|
||
</div>
|
||
<label style="display:flex;align-items:center;gap:8px;margin:8px 0">
|
||
<input type="checkbox" id="syncPreBackup">
|
||
<span style="font-size:12px">Create a local backup before each sync</span>
|
||
</label>
|
||
<p style="font-size:11px;color:var(--text-faint);margin:0 0 8px;line-height:1.4">
|
||
Sync password ≠ master password. <b>Use the same one
|
||
on every device</b> — that's how the encrypted snapshot
|
||
round-trips. Stored DPAPI-protected locally.
|
||
</p>
|
||
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-bottom:6px">
|
||
<button class="btn btn-ghost btn-sm" id="syncSetPwdBtn">Set sync password</button>
|
||
<span id="syncPwdStatus" style="font-size:11px;color:var(--text-faint)">Not set.</span>
|
||
</div>
|
||
<div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-top:6px">
|
||
<button class="btn btn-primary btn-sm" id="syncNowBtn">Sync now</button>
|
||
<span id="syncStatus" style="font-size:11px;color:var(--accent);display:none"></span>
|
||
<span id="syncLast" style="font-size:11px;color:var(--text-faint)"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="slideover-field" id="autoBackupField" style="display:none">
|
||
<div class="slideover-field-label">Auto-backup</div>
|
||
<p style="font-size:12px;color:var(--text-dim);margin:0 0 8px;line-height:1.5">
|
||
Encrypted JSON dropped silently into a folder of your
|
||
choice, on a schedule. Old backups beyond the retention
|
||
count are pruned automatically.
|
||
</p>
|
||
<label style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
||
<input type="checkbox" id="settingAutoBackupEnabled">
|
||
<span>Enable auto-backup</span>
|
||
</label>
|
||
<div id="autoBackupConfig" style="display:none;border-left:2px solid var(--border);padding-left:10px;margin-left:4px">
|
||
<div style="margin-bottom:8px">
|
||
<div style="font-size:11px;color:var(--text-faint);margin-bottom:4px">Folder</div>
|
||
<div style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
|
||
<code id="autoBackupDir" style="font-size:11px;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background:var(--bg-2);padding:4px 6px;border-radius:4px">(not set)</code>
|
||
<button class="btn btn-ghost btn-sm" id="autoBackupPickDirBtn">Choose…</button>
|
||
</div>
|
||
</div>
|
||
<div style="display:flex;gap:12px;flex-wrap:wrap;margin-bottom:8px">
|
||
<label style="display:flex;flex-direction:column;gap:2px">
|
||
<span style="font-size:11px;color:var(--text-faint)">Every (days)</span>
|
||
<input type="number" id="settingAutoBackupInterval" min="1" max="365" value="7" style="width:80px">
|
||
</label>
|
||
<label style="display:flex;flex-direction:column;gap:2px">
|
||
<span style="font-size:11px;color:var(--text-faint)">Keep last N</span>
|
||
<input type="number" id="settingAutoBackupKeep" min="1" max="100" value="10" style="width:80px">
|
||
</label>
|
||
</div>
|
||
<p style="font-size:11px;color:var(--text-faint);margin:0 0 8px;line-height:1.4">
|
||
Backups are encrypted with a password you choose
|
||
(independent of your master password — you'll need it
|
||
to restore). Filename pattern: <code>vault-autobackup-YYYYMMDD-HHmmss.json</code>.
|
||
</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">
|
||
<button class="btn btn-ghost btn-sm" id="autoBackupNowBtn">
|
||
<svg><use href="#i-log-out"/></svg> Backup now
|
||
</button>
|
||
<span id="autoBackupLast" style="font-size:11px;color:var(--text-faint)"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="slideover-field">
|
||
<div class="slideover-field-label">Account</div>
|
||
<div style="display:flex;align-items:center;gap:12px;margin:0 0 10px">
|
||
<span class="user-avatar user-avatar-lg" id="settingAvatarPreview"></span>
|
||
<div style="display:flex;flex-direction:column;gap:4px">
|
||
<p style="font-size:12px;color:var(--text-dim);margin:0">
|
||
Signed in as <b id="settingUser"></b>
|
||
</p>
|
||
<div style="display:flex;gap:6px">
|
||
<button class="btn btn-ghost btn-sm" id="settingAvatarUpload">
|
||
<svg><use href="#i-user"/></svg> Change picture
|
||
</button>
|
||
<button class="btn btn-ghost btn-sm" id="settingAvatarRemove" style="display:none;color:var(--danger)">
|
||
Remove
|
||
</button>
|
||
</div>
|
||
<input type="file" id="settingAvatarInput" accept="image/*" style="display:none">
|
||
</div>
|
||
</div>
|
||
<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>
|
||
</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–12 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 style="display:flex;gap:6px;flex-wrap:wrap;margin:0 0 8px">
|
||
<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 class="setting-row" style="margin:0">
|
||
<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>
|
||
|
||
<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">
|
||
Disabled.
|
||
</p>
|
||
<p style="font-size:11px;color:var(--text-faint);margin:0 0 8px;line-height:1.4">
|
||
Unlock the vault on this device without retyping your
|
||
master password. The key is stored encrypted with
|
||
Windows DPAPI (tied to your Windows account) — convenient
|
||
on a personal machine, not safe on a shared one.
|
||
</p>
|
||
<button class="btn btn-ghost btn-sm" id="quickUnlockToggleBtn">
|
||
Enable on this device
|
||
</button>
|
||
</div>
|
||
|
||
<div class="slideover-field">
|
||
<div class="slideover-field-label">Recovery key</div>
|
||
<p id="recoveryStatus" style="font-size:12px;color:var(--text-dim);margin:0 0 8px;line-height:1.5">
|
||
No recovery key set.
|
||
</p>
|
||
<p style="font-size:11px;color:var(--text-faint);margin:0 0 8px;line-height:1.4">
|
||
A single-use code that lets you recover access if you
|
||
forget your master password. Generate it once, save the
|
||
code somewhere offline — the server never sees it again.
|
||
</p>
|
||
<div style="display:flex;gap:6px;flex-wrap:wrap">
|
||
<button class="btn btn-ghost btn-sm" id="recoverySetupBtn">
|
||
Generate recovery code
|
||
</button>
|
||
<button class="btn btn-ghost btn-sm is-danger" id="recoveryRemoveBtn" style="display:none">
|
||
Remove
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- MODAL: Re-auth (for export and other sensitive ops) -->
|
||
<!-- ============================================================ -->
|
||
<div id="reauthModal" class="modal is-hidden" role="dialog" aria-modal="true">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-panel modal-panel-sm">
|
||
<header class="modal-header">
|
||
<h3 id="reauthTitle">Confirm master password</h3>
|
||
<button class="icon-btn" data-close><svg><use href="#i-x"/></svg></button>
|
||
</header>
|
||
<form id="reauthForm" class="modal-body" autocomplete="off">
|
||
<p style="margin:0 0 12px;color:var(--text-dim);font-size:13px" id="reauthMessage">
|
||
This action requires your master password.
|
||
</p>
|
||
<label class="field">
|
||
<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>
|
||
<button type="submit" form="reauthForm" class="btn btn-primary" id="reauthConfirm">
|
||
<svg><use href="#i-check"/></svg> Confirm
|
||
</button>
|
||
</footer>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- MODAL: Change Master Password -->
|
||
<!-- ============================================================ -->
|
||
<div id="changeMasterModal" class="modal is-hidden" role="dialog" aria-modal="true">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-panel modal-panel-sm">
|
||
<header class="modal-header">
|
||
<h3 id="changeMasterTitle">Change master password</h3>
|
||
<button class="icon-btn" data-close><svg><use href="#i-x"/></svg></button>
|
||
</header>
|
||
<form id="changeMasterForm" class="modal-body" autocomplete="off">
|
||
<p style="margin:0 0 12px;color:var(--text-dim);font-size:13px;line-height:1.5">
|
||
Your vault will be re-encrypted with the new key.
|
||
All other sessions will be signed out.
|
||
</p>
|
||
<label class="field">
|
||
<span>Current master password</span>
|
||
<input id="cmCurrentPwd" type="password" required>
|
||
</label>
|
||
<label class="field">
|
||
<span>New master password (min 8 chars)</span>
|
||
<input id="cmNewPwd" type="password" required minlength="8">
|
||
</label>
|
||
<label class="field">
|
||
<span>Confirm new master password</span>
|
||
<input id="cmConfirmPwd" type="password" required>
|
||
</label>
|
||
<p id="cmError" style="margin:8px 0 0;color:#dc2626;font-size:12px;display:none"></p>
|
||
</form>
|
||
<footer class="modal-footer">
|
||
<button type="button" class="btn btn-ghost" data-close>Cancel</button>
|
||
<button type="submit" form="changeMasterForm" class="btn btn-primary" id="cmConfirmBtn">
|
||
<svg><use href="#i-check"/></svg> Change password
|
||
</button>
|
||
</footer>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- MODAL: New / Edit Entry -->
|
||
<!-- ============================================================ -->
|
||
<div id="entryModal" class="modal is-hidden" role="dialog" aria-modal="true">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-panel">
|
||
<header class="modal-header">
|
||
<h3 id="entryModalTitle">New entry</h3>
|
||
<button class="icon-btn" data-close><svg><use href="#i-x"/></svg></button>
|
||
</header>
|
||
<form id="entryForm" class="modal-body" autocomplete="off">
|
||
<input type="hidden" id="entryId">
|
||
<label class="field">
|
||
<span>Display name <small style="color:var(--text-faint);font-weight:400">— shown on cards, also used for autofill matching</small></span>
|
||
<input id="entryTitle" type="text" placeholder="GitHub (work account)">
|
||
</label>
|
||
<label class="field">
|
||
<span>Website / App <small style="color:var(--text-faint);font-weight:400">— hostname or brand, used for autofill matching</small></span>
|
||
<input id="entrySite" type="text" required placeholder="github.com">
|
||
</label>
|
||
<label class="field">
|
||
<span>Username / Email</span>
|
||
<input id="entryUsername" type="text" placeholder="me@example.com">
|
||
</label>
|
||
<label class="field">
|
||
<span>Password</span>
|
||
<div class="input-with-action">
|
||
<input id="entryPassword" type="password" required placeholder="••••••••">
|
||
<button type="button" class="icon-btn" id="entryPwToggle"><svg><use href="#i-eye"/></svg></button>
|
||
<button type="button" class="icon-btn" id="entryPwGen" title="Generate"><svg><use href="#i-dice"/></svg></button>
|
||
</div>
|
||
<div class="strength-bar" id="entryStrengthBar"></div>
|
||
</label>
|
||
<label class="field">
|
||
<span>Folder</span>
|
||
<select id="entryFolder"></select>
|
||
</label>
|
||
<label class="field">
|
||
<span>Tags <span class="hint">enter to add</span></span>
|
||
<div class="chip-input" id="entryTagsInput">
|
||
<input id="entryTagsField" type="text" placeholder="add a tag…" autocomplete="off">
|
||
</div>
|
||
<input type="hidden" id="entryTags">
|
||
</label>
|
||
</form>
|
||
<footer class="modal-footer">
|
||
<button class="btn btn-ghost" data-close>Cancel</button>
|
||
<button class="btn btn-primary" id="entrySaveBtn">
|
||
<svg><use href="#i-check"/></svg> Save
|
||
</button>
|
||
</footer>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- MODAL: Password Generator -->
|
||
<!-- ============================================================ -->
|
||
<div id="genModal" class="modal is-hidden" role="dialog" aria-modal="true">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-panel modal-panel-sm">
|
||
<header class="modal-header">
|
||
<h3>Password generator</h3>
|
||
<button class="icon-btn" data-close><svg><use href="#i-x"/></svg></button>
|
||
</header>
|
||
<div class="modal-body">
|
||
<div class="gen-preview" id="genPreview">click regenerate</div>
|
||
<label class="field">
|
||
<span>Length <b id="genLenLabel">16</b></span>
|
||
<input id="genLen" type="range" min="6" max="128" value="16">
|
||
</label>
|
||
<div class="gen-options">
|
||
<label class="check"><input type="checkbox" id="genUpper" checked><span>A-Z</span></label>
|
||
<label class="check"><input type="checkbox" id="genLower" checked><span>a-z</span></label>
|
||
<label class="check"><input type="checkbox" id="genNum" checked><span>0-9</span></label>
|
||
<label class="check"><input type="checkbox" id="genSym" checked><span>!@#</span></label>
|
||
</div>
|
||
</div>
|
||
<footer class="modal-footer">
|
||
<button class="btn btn-ghost" id="genRegen"><svg><use href="#i-rotate-ccw"/></svg> Regenerate</button>
|
||
<button class="btn btn-ghost" id="genCopy"><svg><use href="#i-copy"/></svg> Copy</button>
|
||
<button class="btn btn-primary" id="genUse"><svg><use href="#i-check"/></svg> Use</button>
|
||
</footer>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- MODAL: Password history — list previous versions + revert -->
|
||
<!-- ============================================================ -->
|
||
<div id="historyModal" class="modal is-hidden" role="dialog" aria-modal="true">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-panel modal-panel-sm">
|
||
<header class="modal-header">
|
||
<h3 id="historyTitle">Previous versions</h3>
|
||
<button class="icon-btn" data-close><svg><use href="#i-x"/></svg></button>
|
||
</header>
|
||
<div class="modal-body" id="historyBody"></div>
|
||
<footer class="modal-footer">
|
||
<span style="font-size:11px;color:var(--text-faint);line-height:1.4">
|
||
Up to 20 versions kept. Master-password change clears the history.
|
||
</span>
|
||
</footer>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- CHEATSHEET overlay — press '?' to discover hotkeys -->
|
||
<!-- ============================================================ -->
|
||
<div id="cheatsheetModal" class="modal is-hidden" role="dialog" aria-modal="true">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-panel cheatsheet-panel">
|
||
<header class="modal-header">
|
||
<h3>Keyboard shortcuts</h3>
|
||
<button class="icon-btn" data-close><svg><use href="#i-x"/></svg></button>
|
||
</header>
|
||
<div class="modal-body cheatsheet-body" id="cheatsheetBody"></div>
|
||
<footer class="modal-footer">
|
||
<span style="font-size:11px;color:var(--text-faint)">Press <kbd>?</kbd> anytime to reopen.</span>
|
||
</footer>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- MODAL: Quick search (tray menu → fast password copy) -->
|
||
<!-- ============================================================ -->
|
||
<div id="quickSearchModal" class="modal is-hidden" role="dialog" aria-modal="true">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-panel modal-panel-sm quick-search-panel">
|
||
<div class="quick-search-input">
|
||
<svg><use href="#i-search"/></svg>
|
||
<input id="quickSearchInput" type="text"
|
||
placeholder="Search and press Enter to copy password…"
|
||
autocomplete="off">
|
||
<kbd>Esc</kbd>
|
||
</div>
|
||
<div class="quick-search-results" id="quickSearchResults"></div>
|
||
<div class="quick-search-hint">
|
||
Enter / click = copy password · Ctrl+Enter / Ctrl+click = copy username · Esc = close
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- MODAL: Standalone TOTP generator (paste secret → live code) -->
|
||
<!-- ============================================================ -->
|
||
<div id="totpToolModal" class="modal is-hidden" role="dialog" aria-modal="true">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-panel modal-panel-sm">
|
||
<header class="modal-header">
|
||
<h3>TOTP generator</h3>
|
||
<button class="icon-btn" data-close><svg><use href="#i-x"/></svg></button>
|
||
</header>
|
||
<div class="modal-body">
|
||
<p style="margin:0 0 12px;color:var(--text-dim);font-size:13px;line-height:1.5">
|
||
Paste a base32 secret or an <code>otpauth://</code> URI to
|
||
generate a code on the fly. Nothing is saved.
|
||
</p>
|
||
<label class="field">
|
||
<span>Secret</span>
|
||
<input id="totpToolSecret" type="text" autocomplete="off"
|
||
placeholder="JBSWY3DPEHPK3PXP or otpauth://totp/...">
|
||
<div style="display:flex;gap:6px;margin-top:6px">
|
||
<button type="button" class="btn btn-ghost btn-sm" id="totpToolGen" style="flex:1">
|
||
<svg><use href="#i-dice"/></svg> Generate
|
||
</button>
|
||
<button type="button" class="btn btn-ghost btn-sm" id="totpToolCopySecret" style="flex:1">
|
||
<svg><use href="#i-copy"/></svg> Copy secret
|
||
</button>
|
||
</div>
|
||
</label>
|
||
<div class="totp-panel" style="margin-top:12px">
|
||
<div class="totp-code" id="totpToolCode">— — — — — —</div>
|
||
<button class="icon-btn icon-btn-sm" id="totpToolCopy" title="Copy code">
|
||
<svg><use href="#i-copy"/></svg>
|
||
</button>
|
||
</div>
|
||
<div class="totp-bar-wrap"><div class="totp-bar" id="totpToolBar"></div></div>
|
||
<p id="totpToolError" style="margin:8px 0 0;color:#dc2626;font-size:12px;display:none"></p>
|
||
</div>
|
||
<footer class="modal-footer">
|
||
<button type="button" class="btn btn-ghost" data-close>Close</button>
|
||
</footer>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- CONFIRM / PROMPT MODAL (replaces native confirm/prompt) -->
|
||
<!-- ============================================================ -->
|
||
<div id="confirmModal" class="modal is-hidden" role="dialog" aria-modal="true">
|
||
<div class="modal-backdrop" data-confirm-cancel></div>
|
||
<div class="modal-panel modal-panel-sm">
|
||
<header class="modal-header">
|
||
<h3 id="confirmTitle">Confirm</h3>
|
||
<button class="icon-btn" data-confirm-cancel><svg><use href="#i-x"/></svg></button>
|
||
</header>
|
||
<form id="confirmForm" class="modal-body">
|
||
<p id="confirmMessage" style="margin:0 0 12px;color:var(--text-dim);font-size:13px;line-height:1.5">
|
||
Are you sure?
|
||
</p>
|
||
<label class="field is-hidden" id="confirmInputField" style="position:relative">
|
||
<input id="confirmInput" type="text" autocomplete="off">
|
||
<button type="button" id="confirmInputEye" class="icon-btn icon-btn-sm"
|
||
title="Show / hide"
|
||
style="display:none;position:absolute;right:4px;top:50%;transform:translateY(-50%)">
|
||
<svg><use href="#i-eye"/></svg>
|
||
</button>
|
||
</label>
|
||
</form>
|
||
<footer class="modal-footer">
|
||
<button type="button" class="btn btn-ghost" data-confirm-cancel id="confirmCancelBtn">Cancel</button>
|
||
<button type="submit" form="confirmForm" class="btn btn-primary" id="confirmOkBtn">
|
||
<svg><use href="#i-check"/></svg> Confirm
|
||
</button>
|
||
</footer>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Global busy overlay: spinner + text for long operations (export…). -->
|
||
<div id="busyOverlay" class="busy-overlay is-hidden">
|
||
<div class="busy-box">
|
||
<div class="busy-spinner"></div>
|
||
<div class="busy-text" id="busyText">Working…</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- AUTOFILL PICKER — shown when multiple entries match hotkey -->
|
||
<!-- ============================================================ -->
|
||
<div id="autofillPickerModal" class="modal is-hidden" role="dialog" aria-modal="true">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="modal-panel modal-panel-sm">
|
||
<header class="modal-header">
|
||
<h3 id="autofillPickerTitle">Choose an entry to autofill</h3>
|
||
<button class="icon-btn" data-close><svg><use href="#i-x"/></svg></button>
|
||
</header>
|
||
<div class="modal-body" style="padding:8px 0">
|
||
<p style="margin:0 0 8px 16px;font-size:12px;color:var(--text-dim)">
|
||
Click an entry to type its credentials into the active window.
|
||
</p>
|
||
<div id="autofillPickerList"></div>
|
||
</div>
|
||
<footer class="modal-footer">
|
||
<button class="btn btn-ghost" data-close>Cancel</button>
|
||
</footer>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ============================================================ -->
|
||
<!-- COMMAND PALETTE (Cmd+K) — phase 3, shell only for now -->
|
||
<!-- ============================================================ -->
|
||
<div id="cmdPalette" class="cmd-palette is-hidden" role="dialog" aria-modal="true">
|
||
<div class="modal-backdrop" data-close></div>
|
||
<div class="cmd-panel">
|
||
<div class="cmd-input">
|
||
<svg><use href="#i-command"/></svg>
|
||
<input id="cmdInput" type="text" placeholder="Type a command or search…">
|
||
</div>
|
||
<div class="cmd-results" id="cmdResults"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="js/argon2.js"></script>
|
||
<script src="js/app.crypto.js"></script>
|
||
<script src="js/app.totp.js"></script>
|
||
<script src="js/app.favicon.js"></script>
|
||
<script src="js/app.import.js"></script>
|
||
<script src="js/app.backup.js"></script>
|
||
<script src="js/app.health.js"></script>
|
||
<script src="js/app.overlays.js"></script>
|
||
<script src="js/app.attachments.js"></script>
|
||
<script src="js/app.autofill.js"></script>
|
||
<script src="js/app.js"></script>
|
||
<script src="js/app.unlock.js"></script>
|
||
<script src="js/app.sync.js"></script>
|
||
</body>
|
||
</html>
|