eleven / messenger Public

home: the app-handoff tab settles on 'You're all done' instead of a stuck 'Signing in…' (#488)

The Mac sign-in handoff redirects to elevenmessenger:// — which switches
to the app but never navigates the tab — so the page lingered forever on
the 'Signing in…' headline. A beat after the redirect fires, the card now
flips to a final state: title 'Signed in', headline 'You're all done',
sub 'You're signed in to the Eleven app. You can close this window now.',
busy status cleared, sealed green badge as the visual full stop. The new
text eases in (reduced-motion respected). The web restore path actually
leaves the page, so it's unchanged.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 71e7d5265fdd9bf0f4e2984cce67a2054fa3ff11 parent d6b36ba
3 files changed, +34 −4
  • home/web/pages/restore.html +2 −2
  • home/web/static/home.css +7 −0
  • home/web/static/restore.js +25 −2
diff --git a/home/web/pages/restore.html b/home/web/pages/restore.html
index 09ed256..adeb2e1 100644
--- a/home/web/pages/restore.html
+++ b/home/web/pages/restore.html
@@ -25,8 +25,8 @@
<div class="name" id="entry-host"></div>
</div>
- <h1>Signing in…</h1>
- <p class="sub">Your face or fingerprint is the key. Not even this
+ <h1 id="headline">Signing in…</h1>
+ <p class="sub" id="sub">Your face or fingerprint is the key. Not even this
server can see inside your spaces.</p>
<!-- The user already chose "Sign in" on their chat's page, so the ceremony
diff --git a/home/web/static/home.css b/home/web/static/home.css
index eabcb09..fdd8508 100644
--- a/home/web/static/home.css
+++ b/home/web/static/home.css
@@ -143,9 +143,16 @@ input:focus-visible { outline: 2px solid var(--accent); border-color: transparen
animation: home-spin 0.7s linear infinite;
}
@keyframes home-spin { to { transform: rotate(360deg); } }
+
+/* Text swapped in for the final "all done" state eases into place instead of
+ snapping mid-glance. */
+.swap-in { animation: home-swap-in 0.35s ease; }
+@keyframes home-swap-in { from { opacity: 0; transform: translateY(3px); } }
+
@media (prefers-reduced-motion: reduce) {
.status.busy::before { animation: none; opacity: 0.5; }
.badge { transition: none; }
+ .swap-in { animation: none; }
}
/* The single-serve "space is ready" confirmation: a sealed-check moment, then a
diff --git a/home/web/static/restore.js b/home/web/static/restore.js
index 7b91acc..546bbc4 100644
--- a/home/web/static/restore.js
+++ b/home/web/static/restore.js
@@ -24,6 +24,23 @@ function setStatus(msg, kind) {
el.className = "status" + (kind ? " " + kind : "");
}
+// The tab's final resting state after an app handoff: the user's next glance
+// at this window should read "finished", not a stuck spinner. Headline and sub
+// flip to done-tense and the busy status clears (the sealed badge keeps the
+// green check as the visual full stop).
+function allDone() {
+ document.title = "Signed in";
+ for (const [id, text] of [
+ ["headline", "You’re all done"],
+ ["sub", "You’re signed in to the Eleven app. You can close this window now."],
+ ]) {
+ const el = $(id);
+ el.textContent = text;
+ el.classList.add("swap-in");
+ }
+ setStatus("");
+}
+
function readPayload() {
const raw = location.hash.slice(1);
if (!raw) return null;
@@ -90,8 +107,14 @@ async function restore() {
setStatus("Unlocked. Handing over to the Eleven app…", "ok");
$("actions").classList.add("hidden");
$("badge").classList.add("sealed");
- setTimeout(() => location.replace("elevenmessenger://home-session#" +
- bytesToB64url(new TextEncoder().encode(back))), 600);
+ setTimeout(() => {
+ location.replace("elevenmessenger://home-session#" +
+ bytesToB64url(new TextEncoder().encode(back)));
+ // A custom-scheme redirect switches to the app but never navigates this
+ // tab, so it would sit on "Signing in…" forever. Settle on a final state
+ // a beat later — long enough that the handover line registers first.
+ setTimeout(allDone, 700);
+ }, 600);
return;
}