theseus/pw-fill.html
Local Dev a6f7b335a5 Vault: PIN setup steps, 6-8 digit PINs, save and offer logins, keep sign-ins
The PIN could only be six digits and was set from three bare inputs; the
unlock prompt sat at the top of the page; and the password manager only
filled when you found the key chip, never offered to save, and "Clear
cookies on quit" signed you out of every site, including the ones whose
login the vault already holds.

- PINs are 6 to 8 digits. The PIN record stores its length so pads draw the
  right number of dots and submit on the last digit; a PIN of the wrong
  length is refused without a strike, so an older Aegis pad cannot burn the
  count against an 8-digit PIN.
- Settings sets a PIN in steps: master password, choose the PIN on a pad
  (6/7/8), repeat it, done. The locked vault opens Theseus's own prompt,
  which is now centred, with the PIN pad or the master password field.
- After a sign-in or sign-up form is sent and the page moves on, Theseus
  offers to save (or update) the login, with an optional "ask for my PIN or
  password before filling it". Focusing a login form offers the saved
  logins under it; on a locked vault it offers to unlock first. A failed
  login (the password field still showing) gets no offer.
- "Keep sign-ins for sites in your vault" (on): the quit clear spares the
  cookies and site storage of sites with a saved login. Their hostnames are
  kept sealed with the OS keystore so the list is readable at quit while
  the vault is locked. Verified end to end on a scratch profile: signed in,
  restarted, still signed in; another site's cookie was cleared.
2026-10-04 20:23:43 +02:00

45 lines
2.7 KiB
HTML

<!doctype html>
<html><head><meta charset="utf-8">
<style>
:root { color-scheme: light dark; font-family: system-ui, sans-serif; }
html, body { margin: 0; background: transparent; }
.menu { background: #1c222c; border: 1px solid #ffffff26; border-radius: 10px; box-shadow: 0 12px 34px #000c; overflow: hidden; color: #e7eaf1; }
.hdr { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: #8b98a9; padding: 10px 14px 6px; }
.item { display: flex; align-items: center; gap: 10px; padding: 9px 14px; cursor: pointer; font-size: 13px; }
.item:hover { background: #ffffff12; }
.item .ic { font-size: 14px; }
.item .txt { flex: 1; overflow: hidden; }
.item .u { color: #e7eaf1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.item .d { color: #7f8aa0; font-size: 11.5px; }
.empty { padding: 14px; color: #7f8aa0; font-size: 12.5px; text-align: center; }
@media (prefers-color-scheme: light) {
.menu { background: #ffffff; border-color: rgba(0,0,0,.15); color: #1a1f28; }
.item:hover { background: rgba(0,0,0,.05); }
.item .u { color: #1a1f28; } .item .d, .hdr, .empty { color: #7b8494; }
}
</style></head>
<body>
<div class="menu">
<div class="hdr">Fill password for this site</div>
<div id="list"></div>
</div>
<script>
const $ = (id) => document.getElementById(id);
const esc = (s) => String(s || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
function report() { requestAnimationFrame(() => { try { window.pwfill.resize(document.querySelector(".menu").offsetHeight); } catch (e) {} }); }
window.pwfill.onMatches((data) => {
const list = $("list");
const matches = data.matches || [];
document.querySelector(".hdr").textContent = data.locked ? "Theseus Vault" : "Fill password for this site";
if (data.locked) {
// The vault is locked, but it has a login for this site.
list.innerHTML = `<div class="item" data-id="__unlock"><span class="ic">🔒</span><span class="txt"><div class="u">Sign in with a saved login</div><div class="d">Unlock your vault to fill ${esc(data.host)}</div></span></div>`;
} else if (!matches.length) { list.innerHTML = `<div class="empty">No saved credentials for this site.</div>`; report(); return; }
else list.innerHTML = matches.map((m) =>
`<div class="item" data-id="${esc(m.id)}"><span class="ic">🔑</span><span class="txt"><div class="u">${esc(m.username || "(no username)")}</div><div class="d">${esc(m.domain)}${m.confirm ? " · asks for your PIN" : ""}</div></span></div>`
).join("");
list.querySelectorAll(".item").forEach((el) => el.onclick = () => window.pwfill.pick(el.dataset.id));
report();
});
</script>
</body></html>