diff --git a/src/blobbi/rooms/components/BlobbiCareRoom.tsx b/src/blobbi/rooms/components/BlobbiCareRoom.tsx index 864be26f..2aeff9c0 100644 --- a/src/blobbi/rooms/components/BlobbiCareRoom.tsx +++ b/src/blobbi/rooms/components/BlobbiCareRoom.tsx @@ -3,13 +3,7 @@ /** * BlobbiCareRoom — The bathroom / hygiene room. * - * Layout: - * - BlobbiRoomHero (Blobbi visual + stats) - * - Center: single-focus carousel with hygiene + medicine items - * - Bottom left: towel (RoomActionButton style) - * - Bottom right: shower (RoomActionButton style) - * - * Future: interactions could become drag-based. + * Bottom bar: Towel (left) | hygiene+medicine carousel (center) | Shower (right) */ import { useMemo } from 'react'; @@ -40,48 +34,41 @@ export function BlobbiCareRoom({ ctx }: BlobbiCareRoomProps) { []); const isDisabled = isPublishing || actionInProgress !== null || isUsingItem; - - // Towel is shown as a dedicated button, not in the carousel const towelItem = hygieneItems.find(i => i.id === 'hyg_towel'); - // Carousel: hygiene (except towel) + medicine const carouselEntries = useMemo(() => { const hygiene = getLiveShopItems() .filter(i => i.type === 'hygiene' && i.id !== 'hyg_towel') .map(i => ({ id: i.id, icon: {i.icon}, label: i.name })); - const medicine = getLiveShopItems() .filter(i => i.type === 'medicine') .map(i => ({ id: i.id, icon: {i.icon}, label: i.name })); - return [...hygiene, ...medicine]; }, []); return (
- {/* ── Hero ── */} - {/* ── Bottom Action Bar ── */} {!isActiveFloatingCompanion && ( -
-
- {/* Bottom left — Towel */} -
- {towelItem && ( - {towelItem.icon}} - label="Towel" - color="text-cyan-500" - glowHex="#06b6d4" - onClick={() => handleUseItemFromTab(towelItem.id)} - disabled={isDisabled} - loading={isUsingItem && usingItemId === towelItem.id} - /> - )} -
+
+
+ {/* Left — Towel */} + {towelItem ? ( + {towelItem.icon}} + label="Towel" + color="text-cyan-500" + glowHex="#06b6d4" + onClick={() => handleUseItemFromTab(towelItem.id)} + disabled={isDisabled} + loading={isUsingItem && usingItemId === towelItem.id} + /> + ) : ( +
+ )} - {/* Center: single-focus carousel */} + {/* Center carousel */}
- {/* Bottom right — Shower */} + {/* Right — Shower */} } + icon={} label="Shower" color="text-blue-500" glowHex="#3b82f6" diff --git a/src/blobbi/rooms/components/BlobbiHatcheryRoom.tsx b/src/blobbi/rooms/components/BlobbiHatcheryRoom.tsx index f9cca590..21dc041d 100644 --- a/src/blobbi/rooms/components/BlobbiHatcheryRoom.tsx +++ b/src/blobbi/rooms/components/BlobbiHatcheryRoom.tsx @@ -119,24 +119,24 @@ export function BlobbiHatcheryRoom({ ctx }: BlobbiHatcheryRoomProps) { {/* ── Bottom Action Bar ── */} {!isActiveFloatingCompanion && ( -
-
- {/* Bottom left — Blobbis selector */} +
+
+ {/* Left — Blobbis selector */} } + icon={} label="Blobbis" color="text-primary" glowHex="var(--primary)" onClick={() => setShowBlobbisPanel(true)} badge={companions.length > 1 ? ( - + {companions.length} ) : undefined} /> {/* Center — Main hatch/evolve action */} -
+
{/* Active process: Hatch/Evolve CTA or progress */} {hasActiveProcess && allCompleted && !isTasksLoading && ( @@ -128,7 +127,7 @@ export function BlobbiRoomHero({ ctx, className, hideStats, hideName }: BlobbiRo } return ( -
+
{/* Stats crown */} {!hideStats && } @@ -140,7 +139,7 @@ export function BlobbiRoomHero({ ctx, className, hideStats, hideName }: BlobbiRo animation: `blobbi-bob ${4 - (currentStats.happiness / 100) * 1.5}s ease-in-out infinite, blobbi-sway ${6 - (currentStats.happiness / 100) * 2}s ease-in-out infinite`, } : undefined} > -
+
@@ -159,7 +158,7 @@ export function BlobbiRoomHero({ ctx, className, hideStats, hideName }: BlobbiRo {/* Blobbi Name — hidden for eggs */} {!hideName && !isEgg && (

{companion.name} @@ -195,7 +194,7 @@ function StatsCrown({ const count = allStats.length; const isSmall = heroWidth < 400; const arcSpread = isSmall - ? (count <= 2 ? 90 : count <= 3 ? 130 : 160) + ? (count <= 2 ? 80 : count <= 3 ? 120 : 150) : (count <= 2 ? 80 : count <= 3 ? 120 : 160); const arcHalf = arcSpread / 2; const angles = count === 1 @@ -203,11 +202,11 @@ function StatsCrown({ : allStats.map((_, i) => -arcHalf + (arcSpread / (count - 1)) * i); return ( -
+
{allStats.map((s, i) => { const angleDeg = angles[i]; const angleRad = (angleDeg * Math.PI) / 180; - const radius = Math.min(210, Math.max(140, (heroWidth - 340) / (640 - 340) * (210 - 140) + 140)); + const radius = Math.min(180, Math.max(100, (heroWidth - 340) / (640 - 340) * (180 - 100) + 100)); const x = Math.sin(angleRad) * radius; const y = Math.cos(angleRad) * radius - radius; @@ -251,7 +250,7 @@ function StatIndicator({ stat, value, color, status = 'normal' }: StatIndicatorP return (
@@ -265,10 +264,10 @@ function StatIndicator({ stat, value, color, status = 'normal' }: StatIndicatorP />
- {IconComponent && } + {IconComponent && } {isLow && ( )} diff --git a/src/blobbi/rooms/components/BlobbiRoomShell.tsx b/src/blobbi/rooms/components/BlobbiRoomShell.tsx index 4b9fc9e0..09a7cb09 100644 --- a/src/blobbi/rooms/components/BlobbiRoomShell.tsx +++ b/src/blobbi/rooms/components/BlobbiRoomShell.tsx @@ -147,7 +147,7 @@ export function BlobbiRoomShell({ The `direction` in nav state tells which way to animate. For now, we simply render the active room instantly. */} -
+
diff --git a/src/blobbi/rooms/components/ItemCarousel.tsx b/src/blobbi/rooms/components/ItemCarousel.tsx index facf1962..45d2491a 100644 --- a/src/blobbi/rooms/components/ItemCarousel.tsx +++ b/src/blobbi/rooms/components/ItemCarousel.tsx @@ -3,11 +3,10 @@ /** * ItemCarousel — Single-focus carousel for room items. * - * Shows one main item at centre with translucent prev/next previews. - * Left/right arrows cycle through items in a loop. + * Mobile: focused item only + compact arrows (no prev/next previews) + * Desktop: focused item + translucent prev/next previews + arrows * * Each "item" is an opaque entry with id, icon, label, and an onUse callback. - * The carousel doesn't know about shop items or actions — the parent maps them. */ import { useState, useCallback } from 'react'; @@ -58,7 +57,7 @@ export function ItemCarousel({ if (count === 0) { return ( -
+

Nothing here yet

); @@ -68,15 +67,16 @@ export function ItemCarousel({ const prevItem = items[(index - 1 + count) % count]; const nextItem = items[(index + 1) % count]; const isThisActive = activeItemId === current.id; + const showPreviews = count >= 3; return ( -
+
{/* Left arrow */} - {/* Preview (prev) — only when 3+ items */} - {count >= 3 && ( -
+ {/* Preview (prev) — desktop only, 3+ items */} + {showPreviews && ( +
{prevItem.icon}
)} @@ -98,24 +98,24 @@ export function ItemCarousel({ onClick={() => onUse(current.id)} disabled={disabled} className={cn( - 'relative flex flex-col items-center gap-1 py-2 px-4 rounded-2xl transition-all duration-200 shrink-0', + 'relative flex flex-col items-center gap-0.5 py-1 px-3 sm:px-4 rounded-2xl transition-all duration-200 shrink-0', 'hover:-translate-y-0.5 active:scale-95', isThisActive && 'bg-accent/40', disabled && !isThisActive && 'opacity-50 pointer-events-none', )} > - + {current.icon} - {current.label} + {current.label} {isThisActive && ( - + )} - {/* Preview (next) — only when 3+ items */} - {count >= 3 && ( -
+ {/* Preview (next) — desktop only, 3+ items */} + {showPreviews && ( +
{nextItem.icon}
)} @@ -125,7 +125,7 @@ export function ItemCarousel({ onClick={next} disabled={disabled} className={cn( - 'size-8 rounded-full flex items-center justify-center shrink-0', + 'size-7 sm:size-8 rounded-full flex items-center justify-center shrink-0', 'text-muted-foreground/40 hover:text-foreground/70 hover:bg-accent/40', 'transition-all duration-200 active:scale-90', disabled && 'opacity-30 pointer-events-none', diff --git a/src/blobbi/rooms/components/RoomActionButton.tsx b/src/blobbi/rooms/components/RoomActionButton.tsx index 43330081..a0209bb7 100644 --- a/src/blobbi/rooms/components/RoomActionButton.tsx +++ b/src/blobbi/rooms/components/RoomActionButton.tsx @@ -3,12 +3,12 @@ /** * RoomActionButton — Unified circular action button for all rooms. * - * Matches the visual language of the original Photo and Companion buttons: - * - Large rounded-full circle with soft radial glow background - * - Icon centred inside - * - Label beneath - * - Hover lift + scale, active scale-down - * - Consistent size across all rooms + * Responsive sizing: + * - Mobile: size-14 circle, size-7 icons + * - Desktop (sm+): size-20 circle, size-9 icons + * + * Matches the soft radial glow of the original Photo / Companion buttons + * but at a smaller scale so the bottom bar feels proportional on mobile. */ import { Loader2 } from 'lucide-react'; @@ -48,7 +48,7 @@ export function RoomActionButton({ onClick={onClick} disabled={disabled} className={cn( - 'flex flex-col items-center gap-1.5 transition-all duration-300 ease-out', + 'flex flex-col items-center gap-1 transition-all duration-300 ease-out shrink-0', 'hover:-translate-y-1 hover:scale-110 active:scale-95', disabled && 'opacity-50 pointer-events-none', className, @@ -56,13 +56,13 @@ export function RoomActionButton({ >
{loading ? ( - + ) : ( icon )} @@ -73,7 +73,7 @@ export function RoomActionButton({
)}
- {label} + {label} ); }