Reworked the sign-in fields so the goal — autocomplete for usernames on the login pages — produces something a browser will actually offer, and rebuilt the regression guard around what it claims.
The suggestions. name="username"/autocomplete="username" (from the predecessor artifact) are
the standard hints, and the prior review was right that on this origin they are plausibly all hint
and no fill: the password is typed at the PDS, so no password manager ever stores a login here, and
both handlers preventDefault() the submission, so form history never captures the handle either.
Both sign-in fields — Account.svelte's popover and SpacePicker.svelte — now carry a <datalist>
built from rememberedIdentities, the same localStorage list the "Continue as" buttons already
render (handle ?? did). That is native type-ahead that filters as you type in every browser and
depends on nothing the app cancels. The list id is scoped per component (account-signin-identities,
picker-signin-identities) because the picker carries the account chip and both fields can be on
screen at once; a shared id would leave one field pointing at the other's list.
The guard (review finding, error). test/sign-in-surfaces.test.mjs no longer runs
[\s\S]*? across the whole file. It isolates the identity tag first — match /<input\b[^>]*>/g,
keep the one containing bind:value={identifier} — and asserts name, autocomplete and list
against that single tag, plus that the file defines the <datalist> the tag names. Verified in both
directions against the checkout: deleting autocomplete="username" from the real field while adding
a decoy <input name="invite" autocomplete="username"> elsewhere now FAILS (it passed before), and
reordering the attributes on the real field — byte-identical markup — still passes.
The hardcoded list (review finding, info). The surfaces are derived rather than listed: every
.svelte under src/ that imports signIn from $lib/auth.svelte.js is a login page and is
looped over. A third surface is covered without being enumerated. One test names today's two as a
floor, so a derivation that silently matched nothing cannot pass every loop vacuously.
The rendered half. New src/lib/components/sign-in-fields.svelte.test.ts (jsdom, the
components vitest project) mounts both components signed-out with two remembered identities and
asserts on the element a human types into: autocomplete is username, the list resolves to a
datalist that surface defines, its options are ['alice.test', 'did:plc:nohandle'], and the two
surfaces use different ids. Verified it fails when the token or the id scoping is removed.
Checks (all from this working tree, after git rebase origin/main):
pnpm lint— pass;svelte-check687 files, 0 errors, 0 warnings.pnpm test— pass across all packages, 0 failures anywhere (daemon 593, sidecar 126, ingest 49, core/atproto/transport suites green);@radial/ui87 node tests + 816 vitest tests, 5 of them new.scripts/check-no-nul.mjsandgit diff --checkclean.
Remaining risks:
- The browser's own username autofill is still unlikely to have anything stored for this origin;
that is a property of an OAuth handoff, not of the markup. The
datalistis what makes the field offer something, and it only offers identities this browser profile has signed in with before — a fresh profile still sees an empty dropdown, which is correct. - The source-level test recognises a sign-in surface by an
import { … signIn … } from '$lib/auth.svelte.js'line; a namespace import (import * as auth) would not be seen. The floor assertion catches the total-miss case, not a fourth file using an exotic import form.