Accounts Phase 1c: sign-in UI, magic-link landing, auth store

- Shared reactive auth store (auth.user) + postJSON helper (sends the cookie).
- SignIn modal: email -> "check your inbox" (calm, no password); Google slots in
  here in Phase 2.
- /auth/verify route exchanges the magic-link token for a session, then home.
- Header shows "Sign in" or an account avatar; the You sheet gains "Signed in as
  …" + Sign out (or a Sign in row). Anonymous browsing is unchanged.
This commit is contained in:
jay
2026-06-03 01:19:30 +00:00
parent d2ae56dc65
commit 9237180608
6 changed files with 310 additions and 2 deletions
+18 -1
View File
@@ -1,5 +1,6 @@
<script>
let { onBoundaries, onHistory, filtersOn = false } = $props();
let { onBoundaries, onHistory, onaccount, user = null, filtersOn = false } = $props();
let initial = $derived((user?.display_name || user?.email || '?').trim()[0].toUpperCase());
</script>
<header class="appbar">
@@ -20,6 +21,13 @@
stroke="currentColor" stroke-width="1.8" stroke-linecap="round" /></svg>
<span>History</span>
</button>
{#if user}
<button class="acct" onclick={onaccount} title={user.email} aria-label="Your account">
<span class="avatar">{initial}</span>
</button>
{:else}
<button class="signin" onclick={onaccount}>Sign in</button>
{/if}
</nav>
</div>
</header>
@@ -49,6 +57,15 @@
.utils button svg { width: 17px; height: 17px; }
.utils button:hover { background: var(--accent-soft); color: var(--accent-deep); }
.utils button.on { color: var(--accent-deep); }
.utils .signin { border-color: var(--line); color: var(--accent-deep); }
.utils .signin:hover { background: var(--accent-soft); }
.acct { padding: 4px; }
.avatar {
display: inline-flex; align-items: center; justify-content: center;
width: 30px; height: 30px; border-radius: 999px;
background: var(--accent); color: #fff; font-weight: 600; font-size: 0.8rem;
font-family: var(--label);
}
/* On phones the utilities live in the bottom tab bar ("You") instead. */
@media (max-width: 720px) {