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>
3 files changed,
+34
−4
home/web/pages/restore.html+2 −2home/web/static/home.css+7 −0home/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; |
| } |