Skip to content

feat(settings): add mascot color customization (closes #1651)#1667

Merged
senamakel merged 1 commit into
tinyhumansai:mainfrom
senamakel:issue/1651-allow-users-to-customize-their-mascot-co
May 13, 2026
Merged

feat(settings): add mascot color customization (closes #1651)#1667
senamakel merged 1 commit into
tinyhumansai:mainfrom
senamakel:issue/1651-allow-users-to-customize-their-mascot-co

Conversation

@senamakel
Copy link
Copy Markdown
Member

@senamakel senamakel commented May 13, 2026

Summary

  • Adds a "Mascot" entry under Settings → General with five preset color swatches (yellow / burgundy / black / navy / green).
  • New mascotSlice persists the chosen color through userScopedStorage (per-user, survives restarts) and falls back to the default on rehydrate when the persisted value is missing or unknown.
  • HumanPage and the Meet camera frame producer both read the color from Redux so the mascot stays consistent wherever it renders.
  • Existing users keep their current mascot color (yellow) until they change it.

Problem

Closes #1651. The app shipped multiple mascot color variants (already wired through mascotPalette.ts) but no product UI for users to pick one — making the experience feel fixed and impersonal. There was also no settings surface or persistence path that future customization work (accessories, style packs, etc.) could build on.

Solution

  • New app/src/store/mascotSlice.ts with a MascotColor selector, setMascotColor action, validation guard, and REHYDRATE handling that defaults unknown variants back to yellow.
  • Wired into the persisted store via userScopedStorage (whitelist: color), keyed as mascot so each user's preference is namespaced.
  • New MascotPanel with a 5-swatch radiogroup, accessible roles/labels, and a graceful empty-state for builds with no available variants. Reachable from Settings home (General → Mascot) and the new /settings/mascot route.
  • HumanPage and MascotFrameProducer now derive mascotColor from selectMascotColor instead of hardcoding 'yellow'. The native macOS mascot NSPanel webview (MascotWindowApp) is intentionally untouched — it runs outside the Redux store and is a separate follow-up.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy — slice covers setter validation, REHYDRATE key filtering, unknown-payload fallback, and reset; panel covers swatch rendering, selection state, dispatch, no-op-on-same, and back navigation.
  • Diff coverage ≥ 80% — new code is exercised by mascotSlice.test.ts and MascotPanel.test.tsx; integration sites (HumanPage / MascotFrameProducer) are one-line selector reads.
  • N/A: behaviour-only change with no matching feature row in docs/TEST-COVERAGE-MATRIX.md — coverage matrix update deferred to a follow-up that bundles broader settings rows. (Coverage matrix updated)
  • No new external network dependencies introduced.
  • N/A: settings-only UI behind an existing route, not a release-cut surface. (Manual smoke checklist updated if this touches release-cut surfaces)
  • Linked issue closed via Closes #1651 in the ## Related section.

Impact

  • Desktop only — matches the shipped product scope.
  • No core/RPC changes — persistence rides the existing userScopedStorage redux-persist path, so logout/login carries the preference per the same rules as notifications/threads/etc.
  • No migration needed — existing users land on the yellow default until they change it; unknown persisted values are coerced back to the default.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

Commit & Branch

  • Branch: issue/1651-allow-users-to-customize-their-mascot-co
  • Commit SHA: 7a28697

Validation Run

  • pnpm --filter openhuman-app format:check (ran pnpm format which is a superset; output clean)
  • pnpm typecheck
  • Focused tests: pnpm vitest run --config test/vitest.config.ts src/store/__tests__/mascotSlice.test.ts src/components/settings/panels/__tests__/MascotPanel.test.tsx (15/15 passed)
  • N/A: no Rust changes in this PR. (Rust fmt/check)
  • N/A: no Tauri changes in this PR. (Tauri fmt/check)

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: Users can pick a mascot color in Settings; selection persists per-user across restarts and is applied wherever the mascot renders in-app.
  • User-visible effect: New "Mascot" row in Settings → General; new /settings/mascot panel with five color swatches; selecting one updates the mascot immediately on HumanPage and in the Meet camera frame.

Parity Contract

  • Legacy behavior preserved: Default remains yellow; mascot color prop on <YellowMascot> is unchanged (already accepted mascotColor).
  • Guard/fallback/dispatch parity checks: Unknown persisted colors are coerced back to the default on REHYDRATE, and setMascotColor ignores unknown variants — so older persisted state never crashes a future build that dropped a variant.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): None known.
  • Canonical PR: This PR.
  • Resolution: N/A.

Summary by CodeRabbit

  • New Features
    • Added mascot color customization to Settings
    • Users can select from available mascot color options in a dedicated panel
    • Selected mascot color persists across sessions
    • Mascot displays with the chosen color throughout the app

Review Change Stack

)

Adds a "Mascot" entry under Settings → General with five preset
color swatches (yellow, burgundy, black, navy, green). The selection
lives in a new Redux slice persisted through `userScopedStorage`, so
it survives restarts and is per-user. Existing users keep yellow
until they change it, and unknown persisted values fall back to the
default on rehydrate. HumanPage and the Meet camera frame producer
both consume the selected color so the mascot stays consistent
wherever it renders.
@senamakel senamakel requested a review from a team May 13, 2026 16:46
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

📝 Walkthrough

Walkthrough

This pull request implements mascot color customization, allowing users to select from preset color variants in Settings. The changes add Redux state management with persistence, a new settings panel UI, and integrate the selected color into all mascot renderings throughout the app.

Changes

Mascot Color Customization

Layer / File(s) Summary
Redux mascot state and validation
app/src/store/mascotSlice.ts, app/src/features/human/Mascot/index.ts, app/src/store/__tests__/mascotSlice.test.ts
Redux Toolkit slice defines MascotState with color validation, setMascotColor action, selectMascotColor selector, and redux-persist REHYDRATE handling. Comprehensive tests verify state updates, color validation, persistence restoration, and fallback to defaults. Type exports added for palette and color types.
Store integration and persistence setup
app/src/store/index.ts
Mascot reducer registered in store with redux-persist config, storing the user's color preference in userScopedStorage to survive app restarts without cross-user leakage.
Settings navigation routing
app/src/components/settings/hooks/useSettingsNavigation.ts
SettingsRoute type extended with 'mascot' option; URL-to-route resolver recognizes /settings/mascot; breadcrumb logic returns top-level breadcrumb for mascot route.
Mascot color selection panel and UI tests
app/src/components/settings/SettingsHome.tsx, app/src/pages/Settings.tsx, app/src/components/settings/panels/MascotPanel.tsx, app/src/components/settings/panels/__tests__/MascotPanel.test.tsx
Settings entry point added to SettingsHome; new Settings route for MascotPanel; panel renders radio-style color grid using persisted state with palette styling, dispatches selection changes, and handles unavailable variants gracefully. Tests verify radio group presence, selected state reflection, state updates, no-op behavior on same selection, and back navigation.
Mascot component rendering with dynamic color
app/src/features/human/HumanPage.tsx, app/src/features/meet/MascotFrameProducer.tsx
Both components now read mascotColor from Redux via selectMascotColor and pass it to their mascot components (YellowMascot, YellowMascotIdle), replacing hardcoded color defaults.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • tinyhumansai/openhuman#1200: Changes to HumanPage.tsx build directly on the earlier switch to YellowMascot component rendering pattern.

Suggested labels

feature

Poem

🐰 A mascot now in colors bright,
Can shift from yellow, burgundy light,
Persists through every app restart,
A custom touch for every heart!
Settings page now holds the hue,
Your mascot colors—fresh and new. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding mascot color customization to settings, with a reference to the related issue.
Linked Issues check ✅ Passed All acceptance criteria from issue #1651 are met: users can select mascot color from presets [#1651], selection persists per-user [#1651], applied consistently where mascot renders [#1651], existing users default to yellow [#1651], graceful handling of unavailable variants [#1651], and tests added with diff coverage [#1651].
Out of Scope Changes check ✅ Passed All changes directly support mascot color customization: settings UI entry and panel, Redux state slice with persistence, selector integration in rendering components, type exports, navigation hooks, and comprehensive tests. No unrelated changes detected.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added the feature Net-new user-facing capability or product behavior. label May 13, 2026
@senamakel senamakel merged commit 9434898 into tinyhumansai:main May 13, 2026
27 of 35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Net-new user-facing capability or product behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow users to customize their mascot color from Settings

1 participant