Consolidate Boundaries + History under the account; sectioned /account

The inline Boundaries/History panels lived on the home page, so opening them while
scrolled left you stranded. Move everything "yours" behind the account icon:

- Home header slims to: Saved (opens a right-side flyout, signed-in) · shield
  (Boundaries indicator — filled when active — linking to the Boundaries section) ·
  avatar. The inline panels + the home "saved" view are gone.
- /account is now a sectioned hub (left sidebar on desktop, top tabs on mobile),
  OPEN TO EVERYONE with each section self-gating: Profile (sign-in), Saved (sign-in),
  History (device/account), Boundaries (device/account), Admin (admins). This keeps
  Boundaries/History usable without an account (they're device-local) while
  consolidating the UI — and every section loads at the top, fixing the scroll bug.
- Lift Calm Filters and History into shared stores (prefs.svelte.js, history.svelte.js)
  so the home feed (applies/records) and the account page (edits/manages) share one
  source of truth. New SavedFlyout component. Card boundary actions only render when a
  handler is provided.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-04 01:59:53 +00:00
parent d1a4b24627
commit 3924d927aa
10 changed files with 3184 additions and 294 deletions
+47
View File
@@ -0,0 +1,47 @@
// Reactive Calm Filters (Boundaries), shared across the home feed (which applies
// them) and the account page (which edits them). One source of truth.
import * as P from './prefs.js';
import { auth } from './auth.svelte.js';
import { getJSON, putJSON } from './api.js';
export const prefs = $state({ data: P.blank(), ready: false });
export function initPrefs() {
if (prefs.ready) return;
prefs.data = P.load();
prefs.ready = true;
}
export function active() {
return P.active(prefs.data);
}
export function persistPrefs() {
P.save(prefs.data);
if (auth.user) putJSON('/api/prefs', { prefs: prefs.data }).catch(() => {});
}
// Card actions ("Not today" / "Less like this" / "Hide topic").
export function applyPrefAction(kind, value) {
P[kind]?.(prefs.data, value);
prefs.data = { ...prefs.data };
persistPrefs();
}
// On sign-in: adopt the account's prefs if present, else seed them from this device.
export async function syncPrefsOnLogin() {
try {
const res = await getJSON('/api/prefs');
if (res && res.prefs) {
const incoming = Object.assign(P.blank(), res.prefs);
if (JSON.stringify(incoming) !== JSON.stringify(prefs.data)) {
prefs.data = incoming;
P.save(prefs.data);
}
} else {
await putJSON('/api/prefs', { prefs: prefs.data });
}
} catch {
/* best-effort */
}
}