628 lines
21 KiB
HTML
628 lines
21 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<title>PassKeeper</title>
|
|
<link rel="stylesheet" href="popup.css" />
|
|
</head>
|
|
|
|
<body>
|
|
<div id="app">
|
|
<!-- ── Login view ──────────────────────────────────────────────────── -->
|
|
<div id="view-login" class="view hidden">
|
|
<div class="login-header">
|
|
<span class="login-logo">🔒</span>
|
|
<h1>PassKeeper</h1>
|
|
</div>
|
|
<div class="login-body">
|
|
<p id="login-error" class="pk-error hidden"></p>
|
|
<div class="form-group">
|
|
<label for="login-email">Email</label>
|
|
<input
|
|
type="email"
|
|
id="login-email"
|
|
placeholder="you@example.com"
|
|
autocomplete="email"
|
|
/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="login-password">Master Password</label>
|
|
<input
|
|
type="password"
|
|
id="login-password"
|
|
placeholder="Master password"
|
|
autocomplete="current-password"
|
|
/>
|
|
</div>
|
|
<button id="btn-login" class="btn-primary">Unlock Vault</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── MFA view ────────────────────────────────────────────────────── -->
|
|
<div id="view-mfa" class="view hidden">
|
|
<div class="login-header">
|
|
<span class="login-logo">🔐</span>
|
|
<h1>Two-Factor Auth</h1>
|
|
</div>
|
|
<div class="login-body">
|
|
<p id="mfa-hint" class="pk-hint">
|
|
Enter your 6-digit authenticator code.
|
|
</p>
|
|
<p id="mfa-error" class="pk-error hidden"></p>
|
|
|
|
<!-- TOTP input (default) -->
|
|
<div id="mfa-totp-section" class="form-group">
|
|
<label for="mfa-code">Verification Code</label>
|
|
<input
|
|
type="text"
|
|
id="mfa-code"
|
|
inputmode="numeric"
|
|
maxlength="6"
|
|
placeholder="000000"
|
|
autocomplete="one-time-code"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Backup code input (hidden until toggled) -->
|
|
<div id="mfa-backup-section" class="form-group hidden">
|
|
<label for="mfa-backup-code">Backup Code</label>
|
|
<input
|
|
type="text"
|
|
id="mfa-backup-code"
|
|
placeholder="e.g. ab3rqxyz9p"
|
|
autocomplete="off"
|
|
/>
|
|
</div>
|
|
|
|
<button id="btn-mfa-verify" class="btn-primary">Verify</button>
|
|
<button
|
|
id="btn-mfa-use-backup"
|
|
class="btn-ghost"
|
|
style="font-size: 11px; margin-top: 2px"
|
|
>
|
|
Use a backup code
|
|
</button>
|
|
<button
|
|
id="btn-mfa-use-totp"
|
|
class="btn-ghost hidden"
|
|
style="font-size: 11px; margin-top: 2px"
|
|
>
|
|
Use authenticator code
|
|
</button>
|
|
<button id="btn-mfa-back" class="btn-ghost">← Back</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Unlock-only view (tokens from web app, need vault key) ─────── -->
|
|
<div id="view-unlock" class="view hidden">
|
|
<div class="login-header">
|
|
<span class="login-logo">🔓</span>
|
|
<h1>PassKeeper</h1>
|
|
<p style="font-size: 12px; opacity: 0.85; margin-top: 4px">
|
|
Signed in via web app
|
|
</p>
|
|
</div>
|
|
<div class="login-body">
|
|
<p class="pk-hint">Enter your master password to unlock the vault.</p>
|
|
<p id="unlock-error" class="pk-error hidden"></p>
|
|
<div class="form-group">
|
|
<label for="unlock-password">Master Password</label>
|
|
<input
|
|
type="password"
|
|
id="unlock-password"
|
|
placeholder="Master password"
|
|
autocomplete="current-password"
|
|
/>
|
|
</div>
|
|
<button id="btn-unlock" class="btn-primary">Unlock</button>
|
|
<button id="btn-unlock-signout" class="btn-ghost">Sign out</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Vault view ──────────────────────────────────────────────────── -->
|
|
<div id="view-vault" class="view hidden">
|
|
<!-- Top bar -->
|
|
<div class="vault-topbar">
|
|
<div class="search-wrap">
|
|
<svg
|
|
class="search-icon"
|
|
viewBox="0 0 20 20"
|
|
fill="none"
|
|
width="15"
|
|
height="15"
|
|
>
|
|
<circle
|
|
cx="8.5"
|
|
cy="8.5"
|
|
r="5.5"
|
|
stroke="#999"
|
|
stroke-width="1.6"
|
|
/>
|
|
<path
|
|
d="M13 13l3.5 3.5"
|
|
stroke="#999"
|
|
stroke-width="1.6"
|
|
stroke-linecap="round"
|
|
/>
|
|
</svg>
|
|
<input
|
|
type="text"
|
|
id="vault-search"
|
|
placeholder="Search your vault"
|
|
autocomplete="off"
|
|
/>
|
|
</div>
|
|
<a
|
|
id="btn-open-vault"
|
|
class="btn-vault-link"
|
|
title="Open vault"
|
|
target="_blank"
|
|
>
|
|
Vault
|
|
<svg viewBox="0 0 16 16" fill="none" width="12" height="12">
|
|
<path
|
|
d="M6 3H3a1 1 0 00-1 1v9a1 1 0 001 1h9a1 1 0 001-1v-3"
|
|
stroke="currentColor"
|
|
stroke-width="1.5"
|
|
stroke-linecap="round"
|
|
/>
|
|
<path
|
|
d="M9 2h5v5M14 2L8 8"
|
|
stroke="currentColor"
|
|
stroke-width="1.5"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
/>
|
|
</svg>
|
|
</a>
|
|
<button id="btn-add-item" class="btn-add" title="Add item">+</button>
|
|
</div>
|
|
|
|
<!-- Tabs -->
|
|
<div class="vault-tabs" id="vault-tabs">
|
|
<button class="tab-btn active" data-tab="relevant">
|
|
All relevant
|
|
</button>
|
|
<button class="tab-btn" data-tab="all">All items</button>
|
|
<button class="tab-btn" data-tab="favorites">Favorites</button>
|
|
<button class="tab-btn" data-tab="recents">Recents</button>
|
|
</div>
|
|
|
|
<!-- List -->
|
|
<div id="vault-spinner" class="pk-loading hidden">Loading vault…</div>
|
|
<div id="vault-list" class="vault-list"></div>
|
|
<div id="vault-empty" class="pk-empty hidden">No items found.</div>
|
|
</div>
|
|
<!-- /view-vault -->
|
|
|
|
<!-- ── Generator view ─────────────────────────────────────────────── -->
|
|
<div id="view-generator" class="view hidden">
|
|
<div class="gen-suggestion">
|
|
<div class="gen-suggestion-label">Password suggestion</div>
|
|
<div class="gen-suggestion-row">
|
|
<span id="gen-output" class="gen-output"
|
|
>Click refresh to generate</span
|
|
>
|
|
<div class="gen-suggestion-actions">
|
|
<button
|
|
id="btn-gen-copy"
|
|
class="gen-icon-btn"
|
|
title="Copy password"
|
|
>
|
|
<svg viewBox="0 0 24 24" fill="none" width="18" height="18">
|
|
<rect
|
|
x="9"
|
|
y="9"
|
|
width="11"
|
|
height="11"
|
|
rx="2"
|
|
stroke="currentColor"
|
|
stroke-width="1.8"
|
|
/>
|
|
<path
|
|
d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"
|
|
stroke="currentColor"
|
|
stroke-width="1.8"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
<button
|
|
id="btn-gen-refresh"
|
|
class="gen-icon-btn"
|
|
title="Generate new password"
|
|
>
|
|
<svg viewBox="0 0 24 24" fill="none" width="18" height="18">
|
|
<path
|
|
d="M4 4v5h5"
|
|
stroke="currentColor"
|
|
stroke-width="1.8"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
/>
|
|
<path
|
|
d="M20 20v-5h-5"
|
|
stroke="currentColor"
|
|
stroke-width="1.8"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
/>
|
|
<path
|
|
d="M20 9a8 8 0 00-14.93-2.07M4 15a8 8 0 0014.93 2.07"
|
|
stroke="currentColor"
|
|
stroke-width="1.8"
|
|
stroke-linecap="round"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="gen-strength-row">
|
|
<span id="gen-strength-label" class="gen-strength-label"></span>
|
|
</div>
|
|
|
|
<div class="gen-body">
|
|
<div class="gen-row">
|
|
<label for="gen-length" class="gen-label">Password length:</label>
|
|
<input
|
|
type="number"
|
|
id="gen-length-num"
|
|
class="gen-length-num"
|
|
value="16"
|
|
min="8"
|
|
max="64"
|
|
/>
|
|
<span class="gen-label">characters</span>
|
|
</div>
|
|
<input
|
|
type="range"
|
|
id="gen-length"
|
|
class="gen-slider"
|
|
min="8"
|
|
max="64"
|
|
value="16"
|
|
/>
|
|
|
|
<label class="gen-check">
|
|
<input type="checkbox" id="gen-lower" checked />
|
|
<span>Lowercase (abc)</span>
|
|
</label>
|
|
<label class="gen-check">
|
|
<input type="checkbox" id="gen-upper" checked />
|
|
<span>Uppercase (ABC)</span>
|
|
</label>
|
|
<label class="gen-check">
|
|
<input type="checkbox" id="gen-numbers" checked />
|
|
<span>Numbers (123)</span>
|
|
</label>
|
|
<label class="gen-check">
|
|
<input type="checkbox" id="gen-symbols" checked />
|
|
<span>Randomized symbols (!#$)</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<!-- /view-generator -->
|
|
|
|
<!-- ── Add Item view ─────────────────────────────────────────────── -->
|
|
<div id="view-add" class="view hidden">
|
|
<div class="add-header">
|
|
<button id="btn-add-back" class="add-back-btn" title="Back to vault">
|
|
<svg viewBox="0 0 24 24" fill="none" width="18" height="18">
|
|
<path
|
|
d="M15 18l-6-6 6-6"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
<span class="add-header-title">Add Password</span>
|
|
</div>
|
|
<div class="add-body">
|
|
<p id="add-error" class="pk-error hidden"></p>
|
|
<div class="form-group">
|
|
<label for="add-name"
|
|
>Site name <span class="add-required">*</span></label
|
|
>
|
|
<input
|
|
type="text"
|
|
id="add-name"
|
|
placeholder="e.g. GitHub"
|
|
autocomplete="off"
|
|
/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="add-url">URL</label>
|
|
<input
|
|
type="url"
|
|
id="add-url"
|
|
placeholder="https://example.com"
|
|
autocomplete="off"
|
|
/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="add-username">Username / Email</label>
|
|
<input
|
|
type="text"
|
|
id="add-username"
|
|
placeholder="username or email"
|
|
autocomplete="off"
|
|
/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="add-password"
|
|
>Password <span class="add-required">*</span></label
|
|
>
|
|
<div class="add-pw-row">
|
|
<input
|
|
type="password"
|
|
id="add-password"
|
|
placeholder="Password"
|
|
autocomplete="new-password"
|
|
class="add-pw-input"
|
|
/>
|
|
<button
|
|
id="btn-add-toggle-pw"
|
|
class="add-icon-btn"
|
|
type="button"
|
|
title="Show/hide password"
|
|
>
|
|
<svg viewBox="0 0 24 24" fill="none" width="16" height="16">
|
|
<path
|
|
d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"
|
|
stroke="currentColor"
|
|
stroke-width="1.8"
|
|
/>
|
|
<circle
|
|
cx="12"
|
|
cy="12"
|
|
r="3"
|
|
stroke="currentColor"
|
|
stroke-width="1.8"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
<button
|
|
id="btn-add-generate"
|
|
class="add-icon-btn add-gen-btn"
|
|
type="button"
|
|
title="Generate password"
|
|
>
|
|
<svg viewBox="0 0 24 24" fill="none" width="16" height="16">
|
|
<path
|
|
d="M4 4v5h5"
|
|
stroke="currentColor"
|
|
stroke-width="1.8"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
/>
|
|
<path
|
|
d="M20 20v-5h-5"
|
|
stroke="currentColor"
|
|
stroke-width="1.8"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
/>
|
|
<path
|
|
d="M20 9a8 8 0 00-14.93-2.07M4 15a8 8 0 0014.93 2.07"
|
|
stroke="currentColor"
|
|
stroke-width="1.8"
|
|
stroke-linecap="round"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="add-folder">Folder</label>
|
|
<select id="add-folder" class="save-folder-select">
|
|
<option value="">— No folder —</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="add-notes">Notes</label>
|
|
<textarea
|
|
id="add-notes"
|
|
class="add-notes"
|
|
placeholder="Optional notes"
|
|
rows="2"
|
|
autocomplete="off"
|
|
></textarea>
|
|
</div>
|
|
<button id="btn-add-save" class="btn-primary">Save to Vault</button>
|
|
</div>
|
|
</div>
|
|
<!-- /view-add -->
|
|
|
|
<div id="view-account" class="view hidden">
|
|
<div class="acct-header">
|
|
<span class="acct-logo">⚙️</span>
|
|
<h2>Account</h2>
|
|
</div>
|
|
<div class="acct-body">
|
|
<div class="acct-section">
|
|
<div class="acct-section-title">🔒 Auto-lock</div>
|
|
<p class="acct-hint">
|
|
Lock the vault after this period of system inactivity.
|
|
</p>
|
|
<select id="acct-idle-timeout" class="acct-select">
|
|
<option value="60">1 minute</option>
|
|
<option value="300">5 minutes</option>
|
|
<option value="600" selected>10 minutes (default)</option>
|
|
<option value="1800">30 minutes</option>
|
|
<option value="0">Never</option>
|
|
</select>
|
|
<p id="acct-idle-saved" class="acct-saved hidden">✓ Saved</p>
|
|
</div>
|
|
|
|
<div class="acct-section">
|
|
<div class="acct-section-title">🌐 Open web vault</div>
|
|
<p class="acct-hint">
|
|
Access all vault features, sharing, and security dashboard.
|
|
</p>
|
|
<a
|
|
id="acct-open-vault"
|
|
class="btn-primary acct-link-btn"
|
|
target="_blank"
|
|
>Open PassKeeper Vault</a
|
|
>
|
|
</div>
|
|
|
|
<div class="acct-section acct-section-danger">
|
|
<div class="acct-section-title">🚪 Sign out</div>
|
|
<p class="acct-hint">
|
|
Clear the local session. You will need your master password to
|
|
unlock again.
|
|
</p>
|
|
<button id="acct-signout" class="btn-ghost">Sign out</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- /view-account -->
|
|
|
|
<!-- ── Save-prompt modal overlay (shared across vault/generator views) -->
|
|
<div
|
|
id="save-prompt-overlay"
|
|
class="save-overlay hidden"
|
|
role="dialog"
|
|
aria-modal="true"
|
|
>
|
|
<div class="save-modal">
|
|
<div class="save-modal-header">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none">
|
|
<path
|
|
d="M8 10V7a4 4 0 018 0v3"
|
|
stroke="#1a1a2e"
|
|
stroke-width="1.8"
|
|
stroke-linecap="round"
|
|
/>
|
|
<rect
|
|
x="3"
|
|
y="10"
|
|
width="18"
|
|
height="12"
|
|
rx="2"
|
|
stroke="#1a1a2e"
|
|
stroke-width="1.8"
|
|
/>
|
|
</svg>
|
|
<span id="save-prompt-title">Save to PassKeeper?</span>
|
|
</div>
|
|
<p id="save-prompt-subtitle" class="save-modal-sub"></p>
|
|
<div class="form-group">
|
|
<label for="save-name">Site name</label>
|
|
<input type="text" id="save-name" placeholder="e.g. GitHub" />
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="save-username">Username / Email</label>
|
|
<input
|
|
type="text"
|
|
id="save-username"
|
|
placeholder="username or email"
|
|
/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="save-folder">Folder</label>
|
|
<select id="save-folder" class="save-folder-select">
|
|
<option value="">— No folder —</option>
|
|
</select>
|
|
</div>
|
|
<div class="save-modal-actions">
|
|
<button id="btn-save-yes" class="btn-primary btn-sm">Save</button>
|
|
<button id="btn-save-no" class="btn-ghost btn-sm">Not now</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Shared bottom nav (visible whenever vault OR generator is shown) -->
|
|
<nav class="bottom-nav" id="main-bottom-nav">
|
|
<button class="nav-btn active" id="nav-vault">
|
|
<svg viewBox="0 0 24 24" fill="none" width="20" height="20">
|
|
<rect
|
|
x="3"
|
|
y="6"
|
|
width="18"
|
|
height="14"
|
|
rx="2"
|
|
stroke="currentColor"
|
|
stroke-width="1.7"
|
|
/>
|
|
<circle
|
|
cx="12"
|
|
cy="13"
|
|
r="2.5"
|
|
stroke="currentColor"
|
|
stroke-width="1.7"
|
|
/>
|
|
<path
|
|
d="M8 6V5a4 4 0 018 0v1"
|
|
stroke="currentColor"
|
|
stroke-width="1.7"
|
|
/>
|
|
</svg>
|
|
<span>Vault</span>
|
|
</button>
|
|
<button class="nav-btn" id="nav-generator" title="Password Generator">
|
|
<svg viewBox="0 0 24 24" fill="none" width="20" height="20">
|
|
<path
|
|
d="M12 2a5 5 0 015 5v1h1a2 2 0 012 2v9a2 2 0 01-2 2H6a2 2 0 01-2-2v-9a2 2 0 012-2h1V7a5 5 0 015-5z"
|
|
stroke="currentColor"
|
|
stroke-width="1.7"
|
|
stroke-linejoin="round"
|
|
/>
|
|
<circle cx="12" cy="14" r="1.8" fill="currentColor" />
|
|
<path
|
|
d="M12 14v2.5"
|
|
stroke="currentColor"
|
|
stroke-width="1.7"
|
|
stroke-linecap="round"
|
|
/>
|
|
</svg>
|
|
<span>Generator</span>
|
|
</button>
|
|
<button class="nav-btn" id="nav-alerts" title="Security Alerts">
|
|
<svg viewBox="0 0 24 24" fill="none" width="20" height="20">
|
|
<path
|
|
d="M12 3l8.5 15H3.5L12 3z"
|
|
stroke="currentColor"
|
|
stroke-width="1.7"
|
|
stroke-linejoin="round"
|
|
/>
|
|
<path
|
|
d="M12 10v4"
|
|
stroke="currentColor"
|
|
stroke-width="1.7"
|
|
stroke-linecap="round"
|
|
/>
|
|
<circle cx="12" cy="17" r="0.8" fill="currentColor" />
|
|
</svg>
|
|
<span>Alerts</span>
|
|
</button>
|
|
<button class="nav-btn" id="nav-account" title="Account">
|
|
<svg viewBox="0 0 24 24" fill="none" width="20" height="20">
|
|
<circle
|
|
cx="12"
|
|
cy="8"
|
|
r="4"
|
|
stroke="currentColor"
|
|
stroke-width="1.7"
|
|
/>
|
|
<path
|
|
d="M4 20c0-4 3.6-7 8-7s8 3 8 7"
|
|
stroke="currentColor"
|
|
stroke-width="1.7"
|
|
stroke-linecap="round"
|
|
/>
|
|
</svg>
|
|
<span>Account</span>
|
|
</button>
|
|
</nav>
|
|
</div>
|
|
<!-- /app -->
|
|
<script src="../shared/crypto.js"></script>
|
|
<script src="popup.js"></script>
|
|
</body>
|
|
</html>
|