import.meta.glob is Vite-only and crashes in Shakespeare's esbuild bundler.
Generated baby-svg-data.ts and adult-svg-data.ts with SVG content as template
literal constants, keeping the existing resolver API unchanged.
The hide transform was missing the 20px arc overhang, so the bottom
curve remained visible after the bar slid up. Match the nav-hidden-slide
approach used by SubHeaderBar.
The ArcBackground was positioned over the entire header including the
safe-area padding, causing the arc curve to sit too high on native apps.
Wrap content in a relative container so the arc only covers the content
area, and add bg-background/85 on the header to fill the safe-area region.
Add 'pinned' prop to SubHeaderBar that transitions top to 0 instead
of sliding off-screen when the nav hides. Applied to ProfilePage tabs
and ComposeLetterSheet toolbar.
- Remove letters section from settings page (accessible from letters page)
- Add background to letter editor drawer panel
- Fix drawer z-index so letter content doesn't bleed through
- Fix compose sheet SubHeaderBar top offset in overlay context
- Hide top bar and sub-header tabs together on scroll down
- Upgrade react, react-dom to ^19.2.4 and @types/react, @types/react-dom to v19
- Upgrade @nostrify/react to 0.4.0 (peer deps fix for React 19)
- Upgrade vaul to 1.1.2 and react-day-picker to 9.14.0 for React 19 compatibility
- Fix useRef() calls to pass explicit initial values (required in React 19)
- Update RefObject types to include null (React 19 type change)
- Rewrite Calendar component for react-day-picker v9 classNames API
- Add npm overrides for @emoji-mart/react (only remaining React 18 holdout)
Use named imports in the dynamic import of @sentry/react so the bundler
can drop re-exported modules we never reference (replay 207K, feedback 67K,
replay-canvas 25K). Also set defaultIntegrations: undefined to prevent
Sentry from pulling those modules at runtime.
Sentry chunk: 431K → 128K (-70%).
Display badge preview inline in the profile bio area (after the
about text) as a horizontal row of thumbnails, matching the style
used in the profile hover card. This works on both desktop and
mobile since it's part of the main profile content.
- Show accepted badges in the profile right sidebar (fixes#189)
- Add 'Give badge' option to the profile 3-dot overflow menu,
allowing users to award their created badges directly from
a user's profile (fixes#185)
- Lazy-load BlobbiCompanionLayer (~450K blobbi code off critical path)
- Split BlobbiActionsProvider into lightweight file to avoid pulling
heavy blobbi action system into the index chunk
- Fix HomePage eagerly importing all 18 page components; use lazy()
so only the configured homepage's chunk is loaded
- Lazy-load ReplyComposeModal in AppRouter and FloatingComposeButton
to defer emoji-mart (~620K) until compose is opened
- Fix barrel import in App.tsx pulling BlobbiDevEditor into index;
use direct import from EmotionDevContext instead
Add isLocalhostDev() helper that checks both import.meta.env.DEV AND
hostname (localhost/127.0.0.1/0.0.0.0). This ensures dev buttons only
appear during local development, never on deployed apps.
Dev controls now hidden in production:
- Dev Hatch/Evolve instant transition buttons
- Dev State Editor button
- Dev Emotion Tester button
Convert 44 page imports from static to React.lazy() with dynamic imports.
Only HomePage, Index, and NotFound remain eagerly loaded as critical-path
pages. The existing Suspense boundary in MainLayout (with PageSkeleton
fallback) already wraps the content area, so lazy pages show a skeleton
while loading without affecting the sidebar or navigation.
Replace useLocalStorage with direct localStorage reads that re-trigger
when the 'daily-missions-updated' event fires. The previous approach
cached state internally and didn't see same-tab localStorage writes.
Now when mutations write to localStorage and dispatch the event:
- Version counter bumps
- useMemo re-reads from localStorage
- UI updates immediately without page refresh
Mission pool changes:
- Add 'medicine' action type for giving medicine to Blobbi
- Add medicine_1 (30 coins) and medicine_2 (50 coins) missions
- Mark clean, sing, play_music, medicine as available for ALL stages (egg+baby+adult)
- Keep interact, feed, sleep, take_photo as baby/adult only
Egg users now have 8 valid missions:
- clean_1, clean_2
- sing_1, sing_2
- play_music_1, play_music_2
- medicine_1, medicine_2
This ensures egg-only users always have alternatives when rerolling
(need at least 4 missions for 3 daily + 1 reroll target)
- Expand DAILY_MISSION_POOL from 7 to 15 missions with more variety
- Multiple difficulty tiers for feed, clean, interact, sing, play_music, take_photo
- Ensures always having alternatives when rerolling
- Fix selectReplacementMission to properly exclude only active missions
- Add state migration for rerollsRemaining in both hooks
- Old localStorage states without rerollsRemaining now get 3 rerolls
- Improve getRerollsRemaining to handle undefined/null values
- Better error message when pool is exhausted
- Add rerollsRemaining to DailyMissionsState (max 3 per day, resets daily)
- Add rerollMission() function with stage-aware replacement selection
- Replacement avoids duplicates and the mission being replaced
- Add useRerollMission hook for mutation with toast feedback
- Add reroll button (RefreshCw icon) to incomplete missions
- Show remaining rerolls count at top of mission list
- Disable reroll for completed/claimed missions
- Bonus mission still works correctly after rerolling
- Add requiredStages property to mission definitions (all current missions require baby/adult)
- Update selectDailyMissions() to filter by user's available Blobbi stages
- Show 'Hatch Your Blobbi First' message when user only has eggs
- Add 50-coin 'Daily Champion' bonus mission after completing all regular missions
- Bonus mission appears locked until all regular missions are completed
- Update useClaimMissionReward hook to support claiming bonus rewards
- Pass availableStages through modal props for proper filtering
Previously, clearing the name input would immediately restore it to 'Egg',
which made for a frustrating UX when trying to fully clear and retype.
Now:
- Name input can be fully cleared while editing
- Validation error shows when name is empty
- Adopt button is disabled when name is empty/whitespace
- Only validate on submit, not on every keystroke
The previous fix used instanceId.slice(0, 8) for the prefix, but since
Blobbi IDs have the format 'blobbi-{pubkeyPrefix12}-{petId10}', the first
8 characters are always 'blobbi-' for all Blobbis owned by the same user.
This caused gradient ID collisions between different Blobbis.
Now using the full sanitized instanceId as the prefix:
b_blobbi-abc123456789-xyz1234567
This ensures each Blobbi gets truly unique SVG IDs.
When multiple Blobbis are rendered on the same page (like in the selector modal),
they all shared the same SVG gradient IDs (e.g., cattiBody3D, blobbiBodyGradient).
The browser only uses the first definition of each ID, so all subsequent Blobbis
would use the first one's colors instead of their own.
Fixed by:
- Adding uniquifySvgIds() function to both adult and baby SVG customizers
- Generating unique prefixes from each Blobbi's ID (first 8 characters)
- Prefixing all SVG IDs and updating all references (url(), href, xlink:href)
This reverts commit 488ce5750d.
Restores sticky (non-fixed) mobile top bar, removes scroll-based
hide/show behavior, reverts pt-mobile-bar back to -mt-mobile-bar
negative margin approach, and removes ARC_OVERHANG_PX spacers from
NotificationsPage and ProfilePage.