Files
silent-send/src/popup/popup.html
T
Claude e4b44a73ea feat: masked passwords UI with vault-gated reveal
Passwords imported from password managers are now:
- Displayed as dots (••••••••) by default in both options and popup
- Separated into their own "Passwords" section in options page
- Only revealable by entering the vault encryption password
- Always masked in the popup mappings list (no reveal there)

Options page:
- New Passwords section with locked/unlocked states
- Reveal button validates against vault encryption password
- Hide button re-masks all password values
- Password mappings excluded from the general Mappings table
- Delete and enable/disable controls work while masked

Popup:
- Password-category mappings show dots for real value
- Password category added to mapping add form dropdown

Storage:
- Added 'password' to default categories list

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
2026-03-26 20:11:05 +00:00

215 lines
10 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=400">
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="container">
<!-- Header -->
<header class="header">
<div class="header-left">
<h1 class="title">Silent Send</h1>
<span class="status-dot" id="statusDot"></span>
</div>
<div class="header-right">
<button class="btn-icon" id="btnReveal" title="Reveal Mode - show real data">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M8 3C4.5 3 1.7 5.1 0.5 8c1.2 2.9 4 5 7.5 5s6.3-2.1 7.5-5c-1.2-2.9-4-5-7.5-5z" stroke="currentColor" stroke-width="1.5" fill="none"/>
<circle cx="8" cy="8" r="2.5" stroke="currentColor" stroke-width="1.5" fill="none"/>
</svg>
</button>
<label class="toggle" title="Enable/disable Silent Send">
<input type="checkbox" id="enableToggle" checked>
<span class="toggle-slider"></span>
</label>
</div>
</header>
<!-- Tab Bar -->
<nav class="tabs">
<button class="tab active" data-tab="identity">Identity</button>
<button class="tab" data-tab="mappings">Mappings</button>
<button class="tab" data-tab="activity">Activity</button>
<button class="tab" data-tab="test">Test</button>
</nav>
<!-- Locked State Overlay -->
<div id="lockedOverlay" style="display:none">
<div style="padding:24px 16px;text-align:center">
<div style="font-size:32px;margin-bottom:8px">&#128274;</div>
<h2 style="font-size:15px;margin:0 0 6px;color:#1f2937">Silent Send is Locked</h2>
<p style="font-size:12px;color:#6b7280;margin:0 0 16px">
Your data is encrypted. Enter your password to unlock.
<br>Substitutions are <strong>paused</strong> until unlocked.
</p>
<div style="display:flex;flex-direction:column;gap:8px;max-width:260px;margin:0 auto">
<input type="password" id="unlockPassword" placeholder="Encryption password" autocomplete="current-password"
style="width:100%;box-sizing:border-box;font-size:13px;padding:8px 10px;border:1px solid #d1d5db;border-radius:6px;text-align:center">
<input type="text" id="unlockTOTP" placeholder="TOTP code (if enabled)" autocomplete="one-time-code" inputmode="numeric" maxlength="6"
style="width:100%;box-sizing:border-box;font-size:13px;padding:8px 10px;border:1px solid #d1d5db;border-radius:6px;text-align:center;display:none">
<button class="btn btn-primary" id="btnUnlock" style="width:100%;padding:8px;font-size:13px">Unlock</button>
<button class="btn" id="btnUnlockBiometric" style="width:100%;padding:8px;font-size:13px;display:none">Unlock with Biometric</button>
<div id="unlockStatus" style="font-size:11px;min-height:16px;color:#dc2626"></div>
</div>
</div>
</div>
<!-- First-Run Setup Banner -->
<div class="first-run-banner" id="firstRunBanner" style="display:none">
<div class="first-run-icon">!</div>
<div class="first-run-text">
<strong>Setup required</strong> — Silent Send needs to know who you are before it can protect you.
Fill in at least your name below, then click Save Identity.
</div>
</div>
<!-- Identity Tab (Smart Patterns) -->
<section class="tab-content active" id="tab-identity">
<!-- Profile selector -->
<div class="profile-bar">
<select id="profileSelect" class="select" style="flex:1"></select>
<button class="btn-icon" id="btnAddProfile" title="Add profile">+</button>
<button class="btn-icon" id="btnRenameProfile" title="Rename profile">&#x270E;</button>
<button class="btn-icon" id="btnDeleteProfile" title="Delete profile">&times;</button>
<label class="toggle" title="Enable/disable this profile" style="margin-left:4px">
<input type="checkbox" id="profileActive" checked>
<span class="toggle-slider"></span>
</label>
</div>
<p class="help-text">Each profile is a person/identity. All active profiles are protected simultaneously.</p>
<div class="id-section">
<div class="id-label-row"><span class="id-label">Names</span><button class="btn-add" data-field="names">+ Add</button></div>
<div id="idNames"></div>
<div class="id-hint">Catches: first last, last first, possessives, case variants</div>
</div>
<div class="id-section">
<div class="id-label-row"><span class="id-label">Emails</span><button class="btn-add" data-field="emails">+ Add</button></div>
<div id="idEmails"></div>
<div class="id-row">
<input type="email" id="idCatchAllEmail" placeholder="Catch-all email (for any unrecognized address)" class="input" style="flex:1">
</div>
<div class="id-hint">Catches: any *@gmail.com, *@yahoo.com, *@outlook.com, etc.</div>
</div>
<div class="id-section">
<div class="id-label-row"><span class="id-label">Usernames</span><button class="btn-add" data-field="usernames">+ Add</button></div>
<div id="idUsernames"></div>
<div class="id-hint">Catches: user@host, /home/user, ~user, C:\Users\user</div>
</div>
<div class="id-section">
<div class="id-label-row"><span class="id-label">Hostnames</span><button class="btn-add" data-field="hostnames">+ Add</button></div>
<div id="idHostnames"></div>
<div class="id-hint">Catches: hostname in user@hostname and standalone</div>
</div>
<div class="id-section">
<div class="id-label-row"><span class="id-label">Phones</span><button class="btn-add" data-field="phones">+ Add</button></div>
<div id="idPhones"></div>
<div class="id-hint">Catches: all formats — (555) 123-4567, 555-123-4567, 555.123.4567</div>
</div>
<button class="btn btn-primary" id="btnSaveIdentity" style="width:100%;margin-top:8px">Save Identity</button>
</section>
<!-- Mappings Tab -->
<section class="tab-content" id="tab-mappings">
<div class="add-mapping">
<div class="input-row">
<input type="text" id="inputReal" placeholder="Real value (e.g. John Smith)" class="input">
<span class="arrow">&rarr;</span>
<input type="text" id="inputSub" placeholder="Substitute (e.g. Alex Demo)" class="input">
</div>
<div class="input-row secondary">
<select id="inputCategory" class="select">
<option value="name">Name</option>
<option value="email">Email</option>
<option value="phone">Phone</option>
<option value="address">Address</option>
<option value="ssn">SSN</option>
<option value="dob">DOB</option>
<option value="domain">Domain</option>
<option value="password">Password</option>
<option value="general">General</option>
</select>
<label class="checkbox-label">
<input type="checkbox" id="inputCaseSensitive">
Case sensitive
</label>
<button class="btn btn-primary" id="btnAdd">Add</button>
</div>
</div>
<div class="mapping-list" id="mappingList">
<div class="empty-state">
No mappings yet. Add your first one above.
</div>
</div>
</section>
<!-- Activity Tab -->
<section class="tab-content" id="tab-activity">
<div class="activity-header">
<span class="badge" id="sessionCount">0 substitutions this session</span>
<button class="btn-text" id="btnClearLog">Clear</button>
</div>
<div class="activity-list" id="activityList">
<div class="empty-state">No activity yet.</div>
</div>
</section>
<!-- Test Tab -->
<section class="tab-content" id="tab-test">
<div class="test-mode-toggle">
<button class="test-mode-btn active" data-mode="strip">Strip (real &rarr; fake)</button>
<button class="test-mode-btn" data-mode="reveal">Reveal (fake &rarr; real)</button>
</div>
<div id="stripMode">
<p class="help-text">Paste text with your real info. See what gets sent.</p>
<textarea id="testInput" class="textarea" placeholder="Try: My name is John Smith, email john@gmail.com, logged in as jsmith@macbook-pro" rows="4"></textarea>
<div class="diff-view" id="diffView">
<div class="diff-label">What gets sent:</div>
<div class="diff-output" id="diffOutput"></div>
</div>
<div class="diff-stats" id="diffStats"></div>
</div>
<div id="revealMode" style="display:none">
<p class="help-text">Paste text from AI responses (with fake data). Get back real data to copy.</p>
<textarea id="revealInput" class="textarea" placeholder="Paste AI output here, e.g.: Hello Alex Demo, I see your project at /home/ademo/..." rows="4"></textarea>
<div class="diff-view" id="revealView">
<div class="diff-label">Your real data:</div>
<div class="diff-output" id="revealOutput"></div>
</div>
<button class="btn btn-primary" id="btnCopyRevealed" style="width:100%;margin-top:8px">Copy to Clipboard</button>
<div class="diff-stats" id="revealStats"></div>
</div>
<div class="test-identity-status" id="identityStatus"></div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="privacy-note">
Your data never leaves your browser. No servers, no tracking, no analytics.
<span id="privacyEncNote">When sync encryption is enabled, identity data is
AES-256 encrypted at rest — unreadable without your password.</span>
</div>
<div class="privacy-note" style="color:#b45309;background:#fef3c7;padding:6px 8px;border-radius:4px;margin-bottom:6px">
Silent Send is a convenience tool, not a security guarantee. It can miss
PPI in images, file uploads, unusual name forms, or data you forgot to
configure. Always verify sensitive messages before sending.
</div>
<a href="#" id="btnOptions">Options</a>
</footer>
</div>
<script src="popup.js" type="module"></script>
</body>
</html>