04/19 Update extension: password generator in-place

This commit is contained in:
2026-04-19 18:38:08 -04:00
parent 66e0129994
commit fe270b133a
5 changed files with 1649 additions and 646 deletions
+397 -146
View File
@@ -1,160 +1,411 @@
<!DOCTYPE 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">
<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>
<div class="form-group">
<label for="login-password">Master Password</label>
<input type="password" id="login-password" placeholder="Master password" autocomplete="current-password">
<!-- ── 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 class="pk-hint">Enter your 6-digit authenticator code.</p>
<p id="mfa-error" class="pk-error hidden"></p>
<div 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>
<button id="btn-mfa-verify" class="btn-primary">Verify</button>
<button id="btn-mfa-back" class="btn-ghost">← Back</button>
</div>
</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 class="pk-hint">Enter your 6-digit authenticator code.</p>
<p id="mfa-error" class="pk-error hidden"></p>
<div 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">
<!-- ── 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>
<button id="btn-mfa-verify" class="btn-primary">Verify</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">
<!-- ── 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="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>
<button id="btn-unlock" class="btn-primary">Unlock</button>
<button id="btn-unlock-signout" class="btn-ghost">Sign out</button>
</div>
</div>
<!-- /view-vault -->
<!-- ── Vault view ──────────────────────────────────────────────────── -->
<div id="view-vault" class="view hidden">
<!-- ── 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>
<!-- 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 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>
<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>
<!-- /view-generator -->
<!-- Save prompt -->
<div id="save-prompt" class="save-prompt hidden">
<div class="save-prompt-title">
<span>💾</span> Save new password?
<button id="btn-save-no" class="save-dismiss"></button>
<!-- ── 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="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>
<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</label>
<input type="text" id="save-username" placeholder="username or email">
</div>
<button id="btn-save-yes" class="btn-primary btn-sm">Save</button>
<!-- ── 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>
<!-- 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="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>
<!-- Bottom nav -->
<nav class="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><!-- /view-vault -->
</div><!-- /app -->
<script src="../shared/crypto.js"></script>
<script src="popup.js"></script>
</body>
<!-- /app -->
<script src="../shared/crypto.js"></script>
<script src="popup.js"></script>
</body>
</html>