Skip to content

Add custom GIF mascot avatar override#2347

Merged
senamakel merged 4 commits into
tinyhumansai:mainfrom
vaddisrinivas:codex/OH-2345-persona-gif-avatar
May 23, 2026
Merged

Add custom GIF mascot avatar override#2347
senamakel merged 4 commits into
tinyhumansai:mainfrom
vaddisrinivas:codex/OH-2345-persona-gif-avatar

Conversation

@vaddisrinivas

@vaddisrinivas vaddisrinivas commented May 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Refs #2345

Adds a lightweight custom GIF avatar override for the mascot/persona surface:

  • persists a validated customMascotGifUrl in the mascot slice
  • adds a custom GIF avatar control + preview in Mascot settings
  • renders the custom GIF on HumanPage when configured, falling back to YellowMascot
  • keeps backend mascot selection and custom GIF mutually exclusive
  • extends mascot preference persistence to include avatar and existing voice selection fields

Branch / commit

  • Branch: codex/OH-2345-persona-gif-avatar
  • Commit: 9ed52f53aeb2caad9c041a50c1a7a2521ea1fc19

Files changed

  • app/src/store/mascotSlice.ts
  • app/src/store/index.ts
  • app/src/features/human/HumanPage.tsx
  • app/src/features/human/Mascot/CustomGifMascot.tsx
  • app/src/features/human/Mascot/index.ts
  • app/src/components/settings/panels/MascotPanel.tsx
  • focused unit tests for slice validation, renderer selection, component rendering, and settings behavior

Behavior change

Users can paste an HTTPS .gif, file:// .gif, loopback HTTP .gif, or local .gif path as a mascot avatar override. Invalid or unsafe values reset/reject to default behavior. Custom GIF avatars are simple image renderers: no lip-sync or state-specific visemes.

Validation

  • corepack pnpm install --frozen-lockfile
  • corepack pnpm --dir app exec vitest run src/store/__tests__/mascotSlice.test.ts src/features/human/Mascot/CustomGifMascot.test.tsx src/features/human/HumanPage.test.tsx src/components/settings/panels/__tests__/MascotPanel.test.tsx --config test/vitest.config.ts — pass, 66 tests
  • PATH=/tmp/openhuman-pnpm-shim:$PATH corepack pnpm typecheck — pass
  • PATH=/tmp/openhuman-pnpm-shim:$PATH corepack pnpm --filter openhuman-app format:check — pass
  • PATH=/tmp/openhuman-pnpm-shim:$PATH git push -u fork codex/OH-2345-persona-gif-avatar — pre-push hook passed: format, lint, compile, rust:check, command-token lint. Lint/Rust emitted pre-existing warnings only.
  • PATH=/tmp/openhuman-pnpm-shim:$PATH corepack pnpm pr:checklist /tmp/openhuman-pr-2345.md — pass
  • node scripts/codex-pr-preflight.mjs --strict-path --lightweight — blocked only by local checkout path: expected /workspace/openhuman, got /Users/srinivasvaddi/Projects/openhuman-2345-persona-gif-avatar; all other checks passed.

Duplicate / stale PR check

gh pr list --repo tinyhumansai/openhuman --state open --search 'custom GIF avatar OR persona pack OR mascot avatar' --json number,title,headRefName,url returned no open duplicates.

Summary by CodeRabbit

  • New Features

    • Added a "Custom GIF avatar" option in Settings with input, save/reset buttons, validation, and centered preview; custom GIFs override the default mascot and update mascot rendering and the "Local default" active state.
  • Persistence

    • Custom GIF selection is persisted and restored (invalid values are scrubbed); selecting a backend mascot clears the custom GIF.
  • Tests

    • Added/updated tests for saving, trimming/validation, preview rendering, clearing behavior, and persisted restore/scrubbing.
  • Localization

    • Added UI strings and validation/error text for the custom GIF avatar across multiple languages.

Review Change Stack

@vaddisrinivas vaddisrinivas requested a review from a team May 20, 2026 12:55
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 59d6445c-3853-4a04-a621-a9ae61622bcb

📥 Commits

Reviewing files that changed from the base of the PR and between 226bf7c and b3b936d.

📒 Files selected for processing (12)
  • app/src/lib/i18n/chunks/ar-5.ts
  • app/src/lib/i18n/chunks/bn-5.ts
  • app/src/lib/i18n/chunks/es-5.ts
  • app/src/lib/i18n/chunks/fr-5.ts
  • app/src/lib/i18n/chunks/hi-5.ts
  • app/src/lib/i18n/chunks/id-5.ts
  • app/src/lib/i18n/chunks/it-5.ts
  • app/src/lib/i18n/chunks/ko-5.ts
  • app/src/lib/i18n/chunks/pt-5.ts
  • app/src/lib/i18n/chunks/ru-5.ts
  • app/src/lib/i18n/chunks/zh-CN-5.ts
  • app/src/store/index.ts
✅ Files skipped from review due to trivial changes (3)
  • app/src/lib/i18n/chunks/hi-5.ts
  • app/src/lib/i18n/chunks/ko-5.ts
  • app/src/lib/i18n/chunks/pt-5.ts

📝 Walkthrough

Walkthrough

Adds custom GIF mascot avatar support: component and exports, Redux state/validation/persistence, settings UI to save/reset a GIF URL with preview, HumanPage conditional rendering, tests, and i18n entries.

Changes

Custom Mascot GIF Avatar Feature

Layer / File(s) Summary
CustomGifMascot component definition and tests
app/src/features/human/Mascot/CustomGifMascot.tsx, app/src/features/human/Mascot/CustomGifMascot.test.tsx, app/src/features/human/Mascot/index.ts
CustomGifMascot React component renders a GIF <img> with accessibility attributes, referrerPolicy="no-referrer", non-draggable behavior, and exported props; tests verify attributes and barrel export.
Redux state, validation, and reducer logic
app/src/store/mascotSlice.ts, app/src/store/__tests__/mascotSlice.test.ts
Adds customMascotGifUrl: string | null, MAX_CUSTOM_MASCOT_GIF_URL_LEN, and isCustomMascotGifUrl validator; new setCustomMascotGifUrl action clears selectedMascotId on valid set and reducers/REHYDRATE scrub invalid persisted values. Tests cover validation, cross-field behavior, reset, and rehydrate.
Persistence configuration
app/src/store/index.ts
Expands mascotPersistConfig whitelist to persist customMascotGifUrl alongside existing mascot fields.
Custom GIF rendering in HumanPage
app/src/features/human/HumanPage.tsx, app/src/features/human/HumanPage.test.tsx
HumanPage reads selectCustomMascotGifUrl and renders CustomGifMascot when set; tests mock and verify the custom GIF preview and that the previous mascot stub is not rendered.
MascotPanel settings UI and handlers
app/src/components/settings/panels/MascotPanel.tsx, app/src/components/settings/panels/__tests__/MascotPanel.test.tsx
Adds local draft and error state, imports selector/action/validator, implements save (trim + validate + dispatch) and reset handlers, updates backend "none" selection to clear GIF state, shows conditional error text and preview via CustomGifMascot. Tests verify save with HTTPS .gif, rejection of non-GIF, and clearing on backend selection.
i18n updates
app/src/lib/i18n/*
Adds settings.mascot.customGif* keys (labels and invalid-URL message) across multiple locale chunk files and the English i18n map.

Sequence Diagram

sequenceDiagram
  participant User
  participant MascotPanel
  participant Redux as ReduxStore
  participant HumanPage
  User->>MascotPanel: Enter custom GIF URL
  MascotPanel->>MascotPanel: Trim & validate with isCustomMascotGifUrl
  User->>MascotPanel: Click Save
  MascotPanel->>ReduxStore: dispatch setCustomMascotGifUrl(url)
  ReduxStore->>ReduxStore: validate/store, clear selectedMascotId, persist
  MascotPanel->>MascotPanel: Render preview (CustomGifMascot)
  User->>HumanPage: Navigate to page
  HumanPage->>ReduxStore: selectCustomMascotGifUrl
  alt custom GIF set
    HumanPage->>HumanPage: Render CustomGifMascot
  else no custom GIF
    HumanPage->>HumanPage: Render YellowMascot
  end
  User->>MascotPanel: Click Reset
  MascotPanel->>ReduxStore: dispatch setCustomMascotGifUrl(null)
  ReduxStore->>ReduxStore: clear customMascotGifUrl
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • tinyhumansai/openhuman#1821: Both PRs modify app/src/store/mascotSlice.ts to add persisted mascot fields; overlap at slice and persistence logic.
  • tinyhumansai/openhuman#1894: Interacts with mascot selection plumbing—this PR ensures setSelectedMascotId clears customMascotGifUrl when a backend mascot is chosen.

Suggested reviewers

  • graycyrus

Poem

🐰 I found a GIF bright and neat,
Trimmed its tail and checked the beat,
Saved to Redux, previewed on screen,
Loops in place — a bouncing dream,
Hooray for mascots, cozy and sweet!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add custom GIF mascot avatar override' directly describes the main feature being added, is concise and specific, and accurately reflects the primary change across all files in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. working A PR that is being worked on by the team. labels May 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/src/components/settings/panels/MascotPanel.tsx (1)

166-166: ⚡ Quick win

Internationalize hardcoded UI strings for consistency.

The custom GIF section contains hardcoded English strings:

  • Line 166: validation error message
  • Line 497: "Custom GIF avatar" label
  • Line 501: aria-label

The rest of the component internationalizes all UI-facing text via t('settings.mascot.*'). For consistency, these strings should also use the i18n system.

Suggested i18n keys

Add to your i18n resources:

'settings.mascot.customGifHeading': 'Custom GIF avatar',
'settings.mascot.customGifLabel': 'Custom GIF avatar URL',
'settings.mascot.customGifError': 'Enter an HTTPS .gif URL, file:// .gif URL, or local .gif path.',

Then update the component:

- Custom GIF avatar
+ {t('settings.mascot.customGifHeading')}

- aria-label="Custom GIF avatar URL"
+ aria-label={t('settings.mascot.customGifLabel')}

- setCustomGifError('Enter an HTTPS .gif URL, file:// .gif URL, or local .gif path.');
+ setCustomGifError(t('settings.mascot.customGifError'));

Also applies to: 497-497, 501-501

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/components/settings/panels/MascotPanel.tsx` at line 166, Replace the
hardcoded English strings in the custom GIF section with i18n keys: change the
validation call setCustomGifError('Enter an HTTPS .gif URL, file:// .gif URL, or
local .gif path.') to use t('settings.mascot.customGifError'), and replace the
visible label "Custom GIF avatar" and the aria-label on the input with
t('settings.mascot.customGifHeading') and t('settings.mascot.customGifLabel')
respectively; also add the three keys ('settings.mascot.customGifHeading',
'settings.mascot.customGifLabel', 'settings.mascot.customGifError') to the i18n
resource files so the component (MascotPanel.tsx) uses the same t(...) pattern
as the rest of the file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/src/components/settings/panels/MascotPanel.tsx`:
- Line 166: Replace the hardcoded English strings in the custom GIF section with
i18n keys: change the validation call setCustomGifError('Enter an HTTPS .gif
URL, file:// .gif URL, or local .gif path.') to use
t('settings.mascot.customGifError'), and replace the visible label "Custom GIF
avatar" and the aria-label on the input with
t('settings.mascot.customGifHeading') and t('settings.mascot.customGifLabel')
respectively; also add the three keys ('settings.mascot.customGifHeading',
'settings.mascot.customGifLabel', 'settings.mascot.customGifError') to the i18n
resource files so the component (MascotPanel.tsx) uses the same t(...) pattern
as the rest of the file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 55d54160-705e-4bd0-abf0-232f682d85e9

📥 Commits

Reviewing files that changed from the base of the PR and between 41e7631 and 9ed52f5.

📒 Files selected for processing (10)
  • app/src/components/settings/panels/MascotPanel.tsx
  • app/src/components/settings/panels/__tests__/MascotPanel.test.tsx
  • app/src/features/human/HumanPage.test.tsx
  • app/src/features/human/HumanPage.tsx
  • app/src/features/human/Mascot/CustomGifMascot.test.tsx
  • app/src/features/human/Mascot/CustomGifMascot.tsx
  • app/src/features/human/Mascot/index.ts
  • app/src/store/__tests__/mascotSlice.test.ts
  • app/src/store/index.ts
  • app/src/store/mascotSlice.ts

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/lib/i18n/chunks/en-5.ts`:
- Around line 207-210: The i18n error message for
'settings.mascot.customGifError' omits loopback HTTP .gif URLs; update the
string value for that key (settings.mascot.customGifError) so it lists all
accepted formats including loopback HTTP .gif URLs (e.g., http://localhost or
http://127.0.0.1), e.g. "Enter an HTTPS .gif URL, loopback HTTP .gif URL
(http://localhost or http://127.0.0.1), file:// .gif URL, or local .gif path."
Ensure only the message string is changed.

In `@app/src/lib/i18n/en.ts`:
- Around line 1954-1957: Update the user-facing help text for the message key
'settings.mascot.customGifError' to include loopback HTTP (e.g., allow
http://localhost .gif) alongside the existing HTTPS, file://, and local path
options, then propagate the identical text change to any locale chunk files that
currently use the same English fallback string so all translations/partials
remain consistent with the new wording.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 917f0d53-92fd-4b8d-9190-caba2075f4e7

📥 Commits

Reviewing files that changed from the base of the PR and between 9ed52f5 and fc3a5d2.

📒 Files selected for processing (15)
  • app/src/components/settings/panels/MascotPanel.tsx
  • app/src/lib/i18n/chunks/ar-5.ts
  • app/src/lib/i18n/chunks/bn-5.ts
  • app/src/lib/i18n/chunks/en-5.ts
  • app/src/lib/i18n/chunks/es-5.ts
  • app/src/lib/i18n/chunks/fr-5.ts
  • app/src/lib/i18n/chunks/hi-5.ts
  • app/src/lib/i18n/chunks/id-5.ts
  • app/src/lib/i18n/chunks/it-5.ts
  • app/src/lib/i18n/chunks/ko-5.ts
  • app/src/lib/i18n/chunks/pt-5.ts
  • app/src/lib/i18n/chunks/ru-5.ts
  • app/src/lib/i18n/chunks/zh-CN-5.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/ko.ts
✅ Files skipped from review due to trivial changes (4)
  • app/src/lib/i18n/chunks/fr-5.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/chunks/zh-CN-5.ts
  • app/src/lib/i18n/chunks/es-5.ts

Comment thread app/src/lib/i18n/chunks/en-5.ts
Comment thread app/src/lib/i18n/en.ts
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 20, 2026

@graycyrus graycyrus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — Custom GIF Mascot Avatar Override

Nice feature, @vaddisrinivas. The URL validation logic is solid — blocks javascript:, data:, non-loopback HTTP, and non-.gif extensions. The mutual exclusivity between backend mascot / custom GIF / local default is well-handled in both the reducer and the UI. Tests are thorough (66 passing covers the key paths). referrerPolicy="no-referrer" on the img tag is a good touch.

Two items below — one scoping note on the persist whitelist, one i18n nit.

Area Files Verdict
Store (slice) mascotSlice.ts Clean — validation, mutual exclusivity, rehydrate scrubbing all look correct
Store (persist) index.ts See comment — whitelist expansion beyond new field
Frontend (renderer) CustomGifMascot.tsx, HumanPage.tsx Clean — simple conditional render, proper fallback
Frontend (settings) MascotPanel.tsx Clean — save/reset/validation/preview all wired up
i18n en.ts, ko.ts, 11 chunk files See comment — untranslated strings
Tests 4 test files Good coverage of slice logic, component render, panel interaction

Comment thread app/src/store/index.ts Outdated
Comment thread app/src/lib/i18n/chunks/ar-5.ts
@vaddisrinivas

Copy link
Copy Markdown
Contributor Author

@graycyrus Thanks again for the review. I addressed the persist-scope and i18n fallback notes in b3b936d and resolved the threads; current checks are green. Could you please take another look when you have a chance?

@vaddisrinivas vaddisrinivas requested a review from graycyrus May 21, 2026 19:48
@senamakel

Copy link
Copy Markdown
Member

merging this for now. we're going to convert mascots into rive files soon. but for now this is fine.

@senamakel senamakel merged commit c6f5a8b into tinyhumansai:main May 23, 2026
33 of 50 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. working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants