theseus/pw-fill.html
Local Dev 2be859267e Theseus UX batch: tabs, address history, password autofill MVP, home cards
Six user-visible improvements + supporting infra, all uncommitted from
the earlier session-in-progress state. Ships together in one release.

Chrome / tabs
- Same-size tabs: flex 1 1 0 with max 200px, min 60px. Container gets
  overflow: hidden so many tabs shrink evenly instead of scrolling out.
- Drag-and-drop tab reordering. HTML5 drag events on each .tab; drop
  side chosen by pointer x within target (Chrome UX). New move-tab IPC
  splices the tabs array + re-emits.

Address bar
- Persistent history at userData/history.json capped at 500 LRU. Ranked
  by host-prefix > url-prefix > contains > title-contains > recency.
- Floating suggestions dropdown (addressPicker WebContentsView) anchored
  under the URL bar. Debounced 80ms input; ArrowUp/Down forward to the
  picker via address-cursor IPC; Enter fires goURL; blur closes after
  160ms so click-through registers. New files address-picker.html +
  address-picker-preload.js. Cleared by existing clearHistoryOnQuit.

Password autofill (A.2 MVP)
- Green key chip in the address bar appears when the vault is UNLOCKED
  and the active tab's host has matching credentials (exact hostname
  match for phase 1; eTLD+1 upgrade queued as A.2.5).
- Click chip → floating picker of usernames. Click a match → main.js
  runs a small script in the active tab: finds first visible
  input[type=password]:not([disabled]), walks the same form for a
  visible text/email/tel/url/search input whose name/id/autocomplete
  matches /username|user|email|login|account|id/, fills both via the
  native value setter + dispatches input/change so React/Vue-controlled
  inputs update. New files pw-fill.html + pw-fill-preload.js.
- emitPwAvailability fires from pushNav + vault setup/unlock/lock so
  the chip's visibility + count stays accurate.

Bookmarks bar
- Right-click context menu on the favorites bar. On empty area:
  "Add current page" (or "Remove current page" if already saved). On a
  specific bookmark: "Open", "Edit title…" (prompt), "Remove", plus
  the add/remove-current entry. Uses a shared .ctxmenu style mirroring
  the settings ctxmenu (dark/light aware).
- Empty-state text updated to mention right-click.

Home page
- Larger responsive card grid: auto-fill minmax(260-280px, 1fr) with
  breakpoints at 600/900/1200. Cards have a subtitle line, a colored
  badge (on-chain / Sia / server / custom), and edit affordances that
  reveal only in Edit mode.
- User-editable set: Edit toggle reveals per-card ✎/✕ + a dashed "+ Add
  card" tile. Modal for add/edit with title / URL / subtitle / badge.
  Reset-to-defaults button.
- Persisted at userData/home-cards.json. New home-preload.js exposes
  window.home = { getCards, setCards, resetCards, navigate }. IPC
  handlers in main.js validate sender.getURL() matches our own
  home.html — third-party pages see the API shape via the preload but
  can't act on the user's local cards.
- Fallback set of 2 cards renders when window.home is unavailable
  (e.g. opening home.html directly outside Electron for preview) so
  the grid is never blank.

Docs
- TheseusNavigator/ROADMAP-identity-wallet.md — the phased plan for
  the two independent strands (password manager A.2/3, browser wallet
  B.1-6). Committed earlier this session; re-listed here for context.
- TheseusNavigator/SESSION-PROMPT-identity-wallet.md — pastable
  kickoff for the next session picking up either strand.

Files added to build.files: address-picker.html,
address-picker-preload.js, pw-fill.html, pw-fill-preload.js,
home-preload.js.
2026-08-17 02:17:12 +02:00

41 lines
2.2 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, "&lt;");
function report() { requestAnimationFrame(() => { try { window.pwfill.resize(document.querySelector(".menu").offsetHeight); } catch (e) {} }); }
window.pwfill.onMatches((data) => {
const list = $("list");
const matches = data.matches || [];
if (!matches.length) { list.innerHTML = `<div class="empty">No saved credentials for this site.</div>`; report(); return; }
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)}</div></span></div>`
).join("");
list.querySelectorAll(".item").forEach((el) => el.onclick = () => window.pwfill.pick(el.dataset.id));
report();
});
</script>
</body></html>