diff --git a/components/shop/shop-content.tsx b/components/shop/shop-content.tsx index 863871f..ba143c2 100644 --- a/components/shop/shop-content.tsx +++ b/components/shop/shop-content.tsx @@ -8,10 +8,11 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { useToast } from "@/components/ui/toast-provider"; import { HABIT_PET_DATA_UPDATED_EVENT } from "@/lib/app-events"; -import { getAvatarCustomization } from "@/lib/avatar-customization-storage"; -import type { AvatarCustomization } from "@/lib/avatar-customization-storage"; -import { defaultAvatarCustomization } from "@/lib/avatar-customization-storage"; -import { DEFAULT_GRAY_COLOR } from "@/lib/character/presets"; +import { + defaultAvatarCustomization, + getAvatarCustomization, + type AvatarCustomization, +} from "@/lib/avatar-customization-storage"; import { getRoomBackground, normalizeRoomBackgroundId } from "@/lib/room-backgrounds"; import { equipShopItem, @@ -25,6 +26,7 @@ import { type ShopLayerId, } from "@/lib/shop-catalog"; import { ShopItemPreviewModal } from "@/components/shop/shop-item-preview-modal"; +import { getShopItemThumbnailColors } from "@/lib/shop-preview"; import { cn } from "@/lib/utils"; function ShopItemPriceBadge({ @@ -80,6 +82,7 @@ function ShopItemCard({ equipped, canAfford, isPending, + characterColors, onPurchase, onEquipToggle, onPreview, @@ -89,11 +92,16 @@ function ShopItemCard({ equipped: boolean; canAfford: boolean; isPending: boolean; + characterColors: AvatarCustomization["colors"]; onPurchase: () => void; onEquipToggle: () => void; onPreview: () => void; }) { const room = item.type === "room" ? getRoomBackground(item.id) : null; + const thumbnailColors = + item.type === "room" + ? null + : getShopItemThumbnailColors(item, characterColors); const actionLabel = (() => { if (isPending) { @@ -123,13 +131,14 @@ function ShopItemCard({ alt="" className="size-full object-cover image-pixelated" /> - ) : ( + ) : thumbnailColors ? ( - )} + ) : null}
@@ -355,6 +364,7 @@ export function ShopContent() { equipped={equippedRoomBackground === item.id} canAfford={coins !== null && coins >= item.price} isPending={pendingItemId === item.id} + characterColors={baseCustomization.colors} onPurchase={() => void handlePurchase(item.id)} onEquipToggle={() => void handleEquipToggle(item)} onPreview={() => setPreviewItem(item)} @@ -378,6 +388,7 @@ export function ShopContent() { equipped={equippedItems.includes(item.id)} canAfford={coins !== null && coins >= item.price} isPending={pendingItemId === item.id} + characterColors={baseCustomization.colors} onPurchase={() => void handlePurchase(item.id)} onEquipToggle={() => void handleEquipToggle(item)} onPreview={() => setPreviewItem(item)} diff --git a/lib/shop-preview.ts b/lib/shop-preview.ts index aa31e9a..08eb29f 100644 --- a/lib/shop-preview.ts +++ b/lib/shop-preview.ts @@ -1,5 +1,12 @@ import type { AvatarCustomization } from "@/lib/avatar-customization-storage"; -import { parseVariantId, parseShopStyleItem, type ShopItemRecord } from "@/lib/shop-catalog"; +import type { HSL } from "@/lib/character/color-utils"; +import type { LayerColorState } from "@/lib/character/types"; +import { + isShopLayerId, + parseVariantId, + parseShopStyleItem, + type ShopItemRecord, +} from "@/lib/shop-catalog"; import { normalizeRoomBackgroundId, type RoomBackgroundId, @@ -17,6 +24,21 @@ export function getShopPreviewRoomId( return normalizeRoomBackgroundId(base.roomBackground); } +/** Tint colors for shop grid thumbnails — match the user's current layer colors. */ +export function getShopItemThumbnailColors( + item: ShopItemRecord, + colors: LayerColorState, +): { color: HSL; skinColor?: HSL } { + if (!isShopLayerId(item.type)) { + return { color: colors.torso }; + } + + return { + color: colors[item.type], + skinColor: item.type === "eyes" ? colors.skin : undefined, + }; +} + /** Build a non-persisted avatar state showing how a shop item would look. */ export function buildShopItemPreviewCustomization( base: AvatarCustomization,