User avatar (Google picture), avatar in mobile You tab, /account page

- Capture the Google profile picture (picture claim) into users.avatar_url; an
  Avatar component shows it, falling back to the initial. Used in the desktop
  header and the mobile "You" tab (which now shows the user when signed in).
- Move account/settings to its own route /account (robust + scrolls to top),
  reached by the desktop avatar and the mobile You tab; drop the inline "You"
  sheet. AccountPanel gains a Sign out action; the page links to Saved/History/
  Boundaries via home intent params (?view= / ?open=).
- db: users.avatar_url (schema + idempotent migration). 118 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-03 14:41:43 +00:00
parent bb008cfaa5
commit 15728c3bcb
11 changed files with 168 additions and 87 deletions
+56
View File
@@ -0,0 +1,56 @@
<script>
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import AccountPanel from '$lib/components/AccountPanel.svelte';
import { auth, refresh } from '$lib/auth.svelte.js';
onMount(() => {
if (!auth.ready) refresh();
});
// Once auth resolves, send anonymous visitors back home.
$effect(() => {
if (auth.ready && !auth.user) goto('/', { replaceState: true });
});
</script>
<header class="bar">
<div class="container inner">
<a class="brand" href="/" aria-label="Upbeat Bytes — home">
<img class="logo" src="/logo.svg" alt="Upbeat Bytes" />
</a>
<a class="back" href="/">← Back</a>
</div>
</header>
<main class="container page">
{#if auth.user}
<h1>You</h1>
<nav class="quick">
<a href="/?view=saved">Saved</a>
<a href="/?open=history">History</a>
<a href="/?open=boundaries">Boundaries</a>
</nav>
<AccountPanel onclose={() => goto('/')} />
{/if}
</main>
<style>
.bar {
background: var(--surface);
border-bottom: 1px solid var(--line);
position: sticky;
top: 0;
z-index: 20;
}
.inner { display: flex; align-items: center; justify-content: space-between; height: 64px; }
.logo { height: 40px; width: auto; display: block; }
.back { color: var(--accent-deep); font-size: 0.9rem; }
.page { padding: 22px 20px 60px; }
h1 { font-size: clamp(2rem, 5vw, 2.6rem); margin: 8px 0 14px; }
.quick { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }
.quick a {
border: 1px solid var(--line); background: var(--surface); color: var(--ink);
border-radius: 999px; padding: 7px 15px; font-size: 0.9rem;
}
.quick a:hover { border-color: var(--accent); color: var(--accent-deep); }
</style>