Adds a "Cards" tab to the Profile Background picker (BackgroundsView)
that selects a pattern applied to the entire user info card and the
extended profile container, in addition to the existing avatar-pad
background/stand/overlay layers.
- AvatarInfoUser/Utilities: propagate cardBackgroundId from RoomUserData.
- InfoStandWidgetUserView: stateful cardBackgroundId, applied as
.profile-card-background.card-background-{id} on the outer Column
with bg-color suppressed when active.
- UserContainerView: same class on the wrapper of the extended profile.
- BackgroundsView: 4th tab "cards" backed by cards.data config
(falls back to backgrounds.data); sends 4-id message via the
extended sendBackgroundMessage signature.
- ui-config.example: cards.data dataset (15 entries).
- BackgroundsView.css: 188 .card-background-{N} rules cloned from
background-{N} (repeat-tiled) plus 15 CSS-pattern overrides for the
provisional dataset (gradients, stripes, dots, grid, checker).
The face avatar (headOnly LayoutAvatarImageView) sits in a 63px-tall
box (44px on mobile) while sibling toolbar icons are smaller, so its
head sprite rendered visually higher than the other icons. Bumped
marginTop from 2px → 12px (desktop) and 4px → 9px (mobile) so the
head sits on the same horizontal axis as the rest of the toolbar.
Removed `absolute bottom-[60px] left-[33px]` from the inner Flex of
ToolbarMeView. The outer wrapper in ToolbarView already anchors the
popup above the face button (bottom-[calc(100%+8px)] left-1/2 -translate-x-1/2),
so the inner pixel-perfect override was detaching it and making it float
mid-screen.
Backend (AuthHttpHandler):
- New users_remember_tokens table stores sha256 hex of the raw token
so the DB never holds a usable credential. Seed file adds the table
and a login.remember.duration.days setting (default 30).
- /api/auth/login accepts "remember": true. On success, issues a fresh
32-byte base64url token, stores the hash, returns the raw token.
- New POST /api/auth/remember: accepts the raw token, looks up by hash,
on a valid hit mints a fresh SSO ticket, rotates the token (deletes
the consumed one and issues a new one), returns both to the client.
No Turnstile - it's an automated trusted-device flow.
- /api/auth/logout also accepts rememberToken and deletes that single
row so other devices keep their tokens.
Frontend:
- LoginView: "Remember me" checkbox (key login.remember_me already in
ExternalTexts). Enabling it persists the returned rememberToken in
localStorage.nitro.remember.token.
- App.tsx: before deciding to show the login screen, try a silent POST
to /api/auth/remember with the stored token. On 200, inject the
returned ssoTicket into window.NitroConfig and proceed to the
authenticated flow; on 401, forget the token and show login.
- PurseView logout: sends the stored rememberToken in the body so the
server can delete it, and clears localStorage before reload.
Replace missing SCSS styles with inline Tailwind utilities and
image imports. Use design-system components (Column, Flex, Text,
Button) for consistent look across the client.
- Import hc-center images as modules (hc_logo, payday, clock, benefits)
- Replace custom CSS classes with Tailwind (w-[], h-[], bg-*, rounded, etc.)
- Use Text bold/small/variant props instead of raw h4/h5/h6 tags
- Add hover:underline on links, border cards, rounded sections
- Remove dead SCSS import from index.scss