eleven / messenger Public

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>
James Adam 715a47b7d2ef6d9ffe97aa9bbbeefe26d5beaaf3 parent bb765cd
3 files changed, +18 −0
  • .gitignore +1 −0
  • changelog/2026-07-14-add-device-passkey-backup.md +4 −0
  • web/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);