this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Reframe atproto page and login around "the Atmosphere"

Two adjustments to the framing around the AT Protocol:

- `docs/understand/at-protocol.mdx`: renamed the lead section from
"Why We Built This" to "Why Opake was built on the Atmosphere".
The old phrasing implied we built the network; we built a cabinet
on top of it. The rewrite makes the distinction explicit — Opake
picked atproto because it already solves identity, portability, and
federation, and because being a resident of an ecosystem is a better
long-term bet than running our own silo. Shifted sharing language
from "AT Protocol network" to "the Atmosphere" to match the
user-facing terminology Bluesky and the wider community use.
- Login page: the old copy framed the identity as an "AT Protocol
handle", which is accurate but jargon-heavy. New framing:

Sign in with your Atmosphere account
Use the handle from any AT Protocol app — Bluesky, your own PDS,
or anywhere else in the Atmosphere. Opake doesn't issue its own
accounts.

The button is now "Continue to your PDS" (which is what actually
happens on submit), with a reassurance line underneath about the
redirect flow and the password never touching us.

Below the form, a new "What's an Atmosphere account?" explainer
lists concrete starting points so visitors without an atproto
handle have a path forward: Bluesky, Eurosky, pckt.blog, Tangled,
and Smoke Signal, each linked with a short description. A closing
note covers the self-hosted-PDS case so that lane isn't invisible.

+112 -25
+112 -25
apps/web/src/routes/devices/login.lazy.tsx
··· 2 2 import { createLazyFileRoute } from "@tanstack/react-router"; 3 3 import { useAuthStore } from "@/stores/auth"; 4 4 5 + // Curated list of Atmosphere apps, rendered under the sign-in form. Each 6 + // entry gives visitors a concrete starting point if they don't yet have an 7 + // atproto account — and signals to existing users that their handle from 8 + // any of these places already works here. 9 + const ATMOSPHERE_APPS: ReadonlyArray<{ 10 + readonly name: string; 11 + readonly href: string; 12 + readonly description: string; 13 + }> = [ 14 + { 15 + name: "Bluesky", 16 + href: "https://bsky.app", 17 + description: "The microblogging app most people start with. Millions of handles.", 18 + }, 19 + { 20 + name: "Eurosky", 21 + href: "https://eurosky.social", 22 + description: "EU-hosted community and infrastructure — data stays in Europe.", 23 + }, 24 + { 25 + name: "pckt.blog", 26 + href: "https://pckt.blog", 27 + description: "Longform blogging — posts live as atproto records.", 28 + }, 29 + { 30 + name: "Tangled", 31 + href: "https://tangled.sh", 32 + description: "A git forge that signs you in with your atproto handle.", 33 + }, 34 + { 35 + name: "Smoke Signal", 36 + href: "https://smokesignal.events", 37 + description: "Event planning with RSVPs, all on the open network.", 38 + }, 39 + ]; 40 + 5 41 function LoginPage() { 6 42 const session = useAuthStore((s) => s.session); 7 43 const startLogin = useAuthStore((s) => s.startLogin); ··· 16 52 }; 17 53 18 54 return ( 19 - <form onSubmit={handleSubmit} className="card card-bordered bg-base-100 w-80 p-6"> 20 - <h1 className="text-ui text-base-content mb-1 font-medium">Sign in to Opake</h1> 21 - <p className="text-caption text-text-muted mb-5"> 22 - Enter your AT Protocol handle to continue. 23 - </p> 24 - <label className="input input-bordered mb-3 flex items-center gap-2"> 25 - <input 26 - type="text" 27 - placeholder="you.bsky.social" 28 - value={handle} 29 - onChange={(e) => setHandle(e.target.value)} 30 - className="grow" 31 - required 32 - disabled={isLoading} 33 - aria-label="AT Protocol handle" 34 - /> 35 - </label> 36 - {errorMessage && ( 37 - <p className="text-caption text-error mb-3" role="alert"> 38 - {errorMessage} 55 + <div className="mx-auto flex w-full max-w-lg flex-col gap-6 px-4 py-8"> 56 + <form onSubmit={handleSubmit} className="card card-bordered bg-base-100 w-full p-6"> 57 + <h1 className="text-ui text-base-content mb-1 font-medium"> 58 + Sign in with your Atmosphere account 59 + </h1> 60 + <p className="text-caption text-text-muted mb-5 leading-relaxed"> 61 + Use the handle from any AT Protocol app — Bluesky, your own PDS, or anywhere else in the 62 + Atmosphere. Opake doesn't issue its own accounts. 63 + </p> 64 + <label className="input input-bordered mb-3 flex items-center gap-2"> 65 + <input 66 + type="text" 67 + placeholder="you.bsky.social" 68 + value={handle} 69 + onChange={(e) => setHandle(e.target.value)} 70 + className="grow" 71 + required 72 + disabled={isLoading} 73 + aria-label="Your Atmosphere handle" 74 + /> 75 + </label> 76 + {errorMessage && ( 77 + <p className="text-caption text-error mb-3" role="alert"> 78 + {errorMessage} 79 + </p> 80 + )} 81 + <button type="submit" className="btn btn-neutral w-full" disabled={isLoading}> 82 + {isLoading ? ( 83 + <span className="loading loading-spinner loading-sm" /> 84 + ) : ( 85 + "Continue to your PDS" 86 + )} 87 + </button> 88 + <p className="text-caption text-text-faint mt-3 leading-relaxed"> 89 + You'll be redirected to your PDS (the provider that holds your account) to authorize 90 + Opake. Your password never reaches us. 39 91 </p> 40 - )} 41 - <button type="submit" className="btn btn-neutral w-full" disabled={isLoading}> 42 - {isLoading ? <span className="loading loading-spinner loading-sm" /> : "Sign in"} 43 - </button> 44 - </form> 92 + </form> 93 + 94 + <section 95 + aria-labelledby="atmosphere-heading" 96 + className="card card-bordered bg-base-100/60 w-full p-5" 97 + > 98 + <h2 99 + id="atmosphere-heading" 100 + className="text-ui text-base-content mb-1 font-medium" 101 + > 102 + What's an Atmosphere account? 103 + </h2> 104 + <p className="text-caption text-text-muted mb-4 leading-relaxed"> 105 + The Atmosphere is the open network of apps built on the AT Protocol. Your account lives 106 + with one provider — your PDS — and works across every app on the network. One handle, 107 + many apps, no duplicate signups. If you already use any of these, you're already set: 108 + </p> 109 + <ul className="flex flex-col gap-2"> 110 + {ATMOSPHERE_APPS.map((app) => ( 111 + <li key={app.name}> 112 + <a 113 + href={app.href} 114 + target="_blank" 115 + rel="noopener noreferrer" 116 + className="border-border-accent/40 bg-base-100 hover:border-primary/60 hover:bg-accent/30 block rounded-lg border p-2.5 transition-colors" 117 + > 118 + <div className="text-ui text-base-content font-medium">{app.name}</div> 119 + <div className="text-caption text-text-muted leading-relaxed"> 120 + {app.description} 121 + </div> 122 + </a> 123 + </li> 124 + ))} 125 + </ul> 126 + <p className="text-caption text-text-faint mt-4 leading-relaxed"> 127 + Prefer self-hosting? Run your own PDS and use the handle on it here — same flow, fully 128 + independent of any provider. 129 + </p> 130 + </section> 131 + </div> 45 132 ); 46 133 } 47 134