215a5c4d64
A calm /play space — "after the brief, a small thing to enjoy." Framework-ready for more games (Word Search next; zen/coloring later). * Daily Word (5 letters / 6 guesses) + Long Word (6 / 7) — same Wordle mechanic, Upbeat Bytes flavor (no "Wordle" in the UI). Hopeful answers; after solving, a one-line "why this word matters." * LLM proposes, code disposes: answers are picked deterministically by date-seed from a hand-curated hopeful pool that's pre-validated ⊆ the guess dictionary (always typeable), avoiding recent repeats; the LLM only adds the optional "why" (with fallback). daily_puzzles(date, game, variant, payload) stores them so everyone gets the same daily; the cycle pre-generates with the "why". * Bundled guess dictionaries (words-5/6.json, ~12.6k/22.4k) for client-side guess validation — never the LLM. Answer lightly obfuscated (base64) in the payload. * Private, gentle stats (played/solved/streak, guess distribution); spoiler-free emoji-grid share. No leaderboard, no timer, no streak-loss drama. * Play in the bottom nav (replacing Browse, still on the lane rail) + the header. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
55 lines
2.8 KiB
Svelte
55 lines
2.8 KiB
Svelte
<script>
|
|
// Mobile-only primary navigation. Highlights = the brief, Latest = the
|
|
// chronological feed, Browse = mood/topic discovery, You = account.
|
|
import Avatar from './Avatar.svelte';
|
|
let { active = 'today', onToday, onLatest, onPlay, onYou, user = null } = $props();
|
|
</script>
|
|
|
|
<nav class="bottomnav" aria-label="Primary">
|
|
<button class:active={active === 'today'} onclick={onToday}>
|
|
<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="4.2" fill="none" stroke="currentColor" stroke-width="1.8" /><path d="M12 3v2M12 19v2M3 12h2M19 12h2M5.6 5.6l1.4 1.4M17 17l1.4 1.4M18.4 5.6L17 7M7 17l-1.4 1.4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" /></svg>
|
|
<span>Highlights</span>
|
|
</button>
|
|
<button class:active={active === 'latest'} onclick={onLatest}>
|
|
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h10" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" /></svg>
|
|
<span>Latest</span>
|
|
</button>
|
|
<button class:active={active === 'play'} onclick={onPlay}>
|
|
<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="4" y="4" width="7" height="7" rx="2" fill="none" stroke="currentColor" stroke-width="1.8"/><rect x="13" y="4" width="7" height="7" rx="2" fill="none" stroke="currentColor" stroke-width="1.8"/><rect x="4" y="13" width="7" height="7" rx="2" fill="none" stroke="currentColor" stroke-width="1.8"/><rect x="13" y="13" width="7" height="7" rx="2" fill="none" stroke="currentColor" stroke-width="1.8"/></svg>
|
|
<span>Play</span>
|
|
</button>
|
|
<button class:active={active === 'you'} onclick={onYou}>
|
|
{#if user}
|
|
<Avatar {user} size={23} />
|
|
{:else}
|
|
<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="8.5" r="3.6" fill="none" stroke="currentColor" stroke-width="1.8" /><path d="M5 20c0-3.6 3.1-5.5 7-5.5s7 1.9 7 5.5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" /></svg>
|
|
{/if}
|
|
<span>You</span>
|
|
</button>
|
|
</nav>
|
|
|
|
<style>
|
|
.bottomnav { display: none; }
|
|
@media (max-width: 720px) {
|
|
.bottomnav {
|
|
display: flex;
|
|
position: fixed;
|
|
left: 0; right: 0; bottom: 0;
|
|
z-index: 30;
|
|
background: var(--surface);
|
|
border-top: 1px solid var(--line);
|
|
padding: 6px 8px calc(6px + env(safe-area-inset-bottom));
|
|
box-shadow: 0 -2px 14px rgba(40, 38, 28, 0.05);
|
|
}
|
|
.bottomnav button {
|
|
flex: 1;
|
|
display: flex; flex-direction: column; align-items: center; gap: 3px;
|
|
background: none; border: none; padding: 6px 0; cursor: pointer;
|
|
color: var(--muted); font-size: 0.7rem; letter-spacing: 0.02em;
|
|
}
|
|
.bottomnav button svg { width: 23px; height: 23px; }
|
|
.bottomnav button.active { color: var(--accent-deep); }
|
|
.bottomnav button.active svg { stroke: var(--accent); }
|
|
}
|
|
</style>
|