From bd369825c3475ee63c5d23078d88c3da38710570 Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Sun, 24 May 2026 09:42:44 -0600 Subject: [PATCH] Tint shop item thumbnails with the user's character layer colors. Replace default gray previews so clothing and accessories in the shop grid reflect each layer's saved color, including skin tone for eyes. --- components/shop/shop-content.tsx | 25 ++++++++++++++++++------- lib/shop-preview.ts | 24 +++++++++++++++++++++++- 2 files changed, 41 insertions(+), 8 deletions(-) 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,