web: passkey backup button in the add-a-device sheet (#897)
* web: passkey backup button in the add-a-device sheet
The "Add a device or back up" dialog now offers a "Back up with a passkey"
button beside the device link, wired to the existing launchEnrol handoff so a
member can enrol (or, when already backed up, update) their access with the
home server without hunting for the sidebar nudge.
- Gated on window.__homeOrigin — the button only appears where a home server
is configured; launchEnrol is a no-op otherwise.
- Label reflects state: "Back up with a passkey" vs "Update passkey backup"
(isEnrolled), matching register/update semantics.
- Export prefetchServerIcon and call it when the dialog opens so the home
server's confirmation card still shows the instance photo (the banner path
already prefetches this; an already-enrolled member never sees the banner).
Ships to web and native by construction — the sheet runs in the native
webview and launchEnrol already handles the native/PWA handoff.
Co-Authored-By: Claude <noreply@anthropic.com>
* web: polish the add-device sheet layout + shorten the backup button
Design pass on the "Add a device or back up" dialog:
- Button label is now just "Back up" (was "Back up with a passkey" /
"Update passkey backup") — shorter, and the state-varying label went away.
- All three actions — Back up, Copy link, Done — sit on ONE line in a single
`.row.wrap`, wrapping only when the viewport is too narrow to hold them.
Done is now secondary so "Back up" is the sole primary action.
- Reordered the sheet to QR -> link -> buttons -> explanation. The long
explanatory paragraph was pushing the QR and buttons below the fold on
short viewports, so the dialog could open looking like a wall of text with
no sign there was anything to do; the actionable content leads now.
Adds `.card .row.wrap { flex-wrap: wrap }` and a top margin for a `.sub` that
trails an action row.
Co-Authored-By: Claude <noreply@anthropic.com>
* web: frame the add-device QR as a plate, cut the surrounding whitespace
The QR floated in the sheet's white: qrCanvas fills its whole canvas white
(a 4-module quiet zone included), and the card is white too, so the code's
white border merged invisibly into the card and read as empty space.
- Shrink the quiet zone (qrCanvas margin 4 -> 2) so the built-in white ring
is smaller.
- Frame the QR in a snug white plate (.qr-tile) lifted by a soft shadow, so
the white has a defined edge and the code reads as a deliberate object. A
border alone can't do it — white-on-white is invisible — the shadow is what
separates the plate from the card. display:block + width:fit-content
overrides .qr-wrap's flex so the plate hugs the code (244px around a 222px
canvas). Scoped to the sheet; the admin invite QR (bare .qr-wrap) is
untouched.
- Tighter vertical rhythm: heading->QR gap drops from ~80px to 32px.
Co-Authored-By: Claude <noreply@anthropic.com>
* web: revert add-device sheet to main, add a "Back up" button
Drop the QR-plate framing, the one-line/wrapping button row, and the
QR->buttons->explanation reorder — the sheet layout is back to exactly main.
The only change now is a secondary "Back up" button between "Copy link" and
"Done" that hands off to the home-server passkey enrol (launchEnrol, already
imported), shown only when a home server is configured.
Also gitignore .lchat-dev.vapid (a dev-server artifact an earlier `git add -A`
had swept in) and untrack it.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
3 files changed,
+18
−0
.gitignore+1 −0changelog/2026-07-14-add-device-passkey-backup.md+4 −0web/static/shell.js+13 −0
diff --git a/.gitignore b/.gitignore| index e276175..4132d50 100644 |
| --- a/.gitignore |
| +++ b/.gitignore |
| @@ -14,6 +14,7 @@ |
| .lchat-dev.json |
| .lchat-dev-bot.json |
| .lchat-dev.db* |
| +.lchat-dev.vapid |
| .lchat-dev-blobs/ |
| # macOS |
diff --git a/changelog/2026-07-14-add-device-passkey-backup.md b/changelog/2026-07-14-add-device-passkey-backup.md| new file mode 100644 |
| index 0000000..0948fb0 |
| --- /dev/null |
| +++ b/changelog/2026-07-14-add-device-passkey-backup.md |
| @@ -0,0 +1,4 @@ |
| +🤖 **Back up your access from "Add a device."** The add-a-device sheet now has |
| +a **Back up** button that saves your access to this space with a passkey — the |
| +same one-tap Eleven sign-in as the sidebar nudge, but always there when you want |
| +it, even if you dismissed the nudge or are already backed up (it just refreshes). |
diff --git a/web/static/shell.js b/web/static/shell.js| index f4f20bf..ea100e2 100644 |
| --- a/web/static/shell.js |
| +++ b/web/static/shell.js |
| @@ -11141,6 +11141,19 @@ async function addDevice() { |
| const row = document.createElement("div"); |
| row.className = "row"; |
| row.appendChild(copy); |
| + // A passkey backup is the one-tap alternative to saving the link: enrol (or |
| + // update) this instance with the home server. Only shown when a home server |
| + // is configured — launchEnrol is a no-op otherwise. |
| + if (window.__homeOrigin) { |
| + const backup = document.createElement("button"); |
| + backup.className = "secondary"; |
| + backup.textContent = "Back up"; |
| + backup.onclick = () => { |
| + backdrop.remove(); // web navigates same-tab; native/PWA opens a new context |
| + launchEnrol(session); |
| + }; |
| + row.appendChild(backup); |
| + } |
| row.appendChild(done); |
| card.appendChild(h); |
| card.appendChild(note); |