From 6e4449ac3d75beb9bf108db1a04c91435de9098d Mon Sep 17 00:00:00 2001 From: Patrick Mulligan Date: Fri, 27 Mar 2026 22:59:59 -0400 Subject: [PATCH] Optimize market images with pict-rs thumbnails Market components were serving full-resolution images regardless of display size. Now uses pict-rs on-the-fly processing to serve WebP thumbnails at appropriate sizes: - ProductCard: 400px thumbnails (was full-res for 192px cards) - CartItem: 128px thumbnails (was full-res for 64px display) - CartSummary: 64px thumbnails (was full-res for 32px display) - CheckoutPage: 128px thumbnails (was full-res for 64px display) - StallView logo: 128px thumbnails (was full-res for 56px display) Adds useImageOptimizer composable wrapping ImageUploadService. Closes #8 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../base/composables/useImageOptimizer.ts | 43 +++++++++++++++++++ src/modules/market/components/CartItem.vue | 6 ++- src/modules/market/components/CartSummary.vue | 4 +- src/modules/market/components/ProductCard.vue | 4 +- src/modules/market/views/CheckoutPage.vue | 4 +- src/modules/market/views/StallView.vue | 4 +- 6 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 src/modules/base/composables/useImageOptimizer.ts diff --git a/src/modules/base/composables/useImageOptimizer.ts b/src/modules/base/composables/useImageOptimizer.ts new file mode 100644 index 0000000..d7383e9 --- /dev/null +++ b/src/modules/base/composables/useImageOptimizer.ts @@ -0,0 +1,43 @@ +import { tryInjectService, SERVICE_TOKENS } from '@/core/di-container' +import type { ImageUploadService } from '../services/ImageUploadService' + +/** + * Composable for generating optimized image URLs via pict-rs + * Handles both file aliases and full URLs + */ +export function useImageOptimizer() { + const imageService = tryInjectService(SERVICE_TOKENS.IMAGE_UPLOAD_SERVICE) + + /** + * Get a thumbnail URL (fast, lower quality - good for cards/lists) + */ + const thumbnail = (url: string | undefined, size = 256): string => { + if (!url) return '' + if (!imageService) return url + return imageService.getThumbnailUrl(url, size) + } + + /** + * Get a resized URL (Lanczos2 filter - better quality for larger displays) + */ + const resized = (url: string | undefined, size = 800): string => { + if (!url) return '' + if (!imageService) return url + return imageService.getResizedUrl(url, size) + } + + /** + * Get a blurred placeholder URL (for loading states) + */ + const blurred = (url: string | undefined, blur = 5): string => { + if (!url) return '' + if (!imageService) return url + return imageService.getBlurredUrl(url, blur) + } + + return { + thumbnail, + resized, + blurred + } +} diff --git a/src/modules/market/components/CartItem.vue b/src/modules/market/components/CartItem.vue index a7a79bd..dc563e8 100644 --- a/src/modules/market/components/CartItem.vue +++ b/src/modules/market/components/CartItem.vue @@ -5,7 +5,7 @@
() const emit = defineEmits<{ diff --git a/src/modules/market/components/CartSummary.vue b/src/modules/market/components/CartSummary.vue index b4b4249..0b64449 100644 --- a/src/modules/market/components/CartSummary.vue +++ b/src/modules/market/components/CartSummary.vue @@ -17,7 +17,7 @@ >
() const emit = defineEmits<{ diff --git a/src/modules/market/components/ProductCard.vue b/src/modules/market/components/ProductCard.vue index be865eb..6b0f855 100644 --- a/src/modules/market/components/ProductCard.vue +++ b/src/modules/market/components/ProductCard.vue @@ -158,6 +158,7 @@ import { Badge } from '@/components/ui/badge' import ProgressiveImage from '@/components/ui/image/ProgressiveImage.vue' import { ShoppingCart, Package, ChevronLeft, ChevronRight } from 'lucide-vue-next' import type { Product } from '@/modules/market/stores/market' +import { useImageOptimizer } from '@/modules/base/composables/useImageOptimizer' interface Props { product: Product @@ -171,6 +172,7 @@ const emit = defineEmits<{ 'view-stall': [stallId: string] }>() +const { thumbnail } = useImageOptimizer() const imageError = ref(false) const currentImageIndex = ref(0) @@ -188,7 +190,7 @@ const currentImage = computed(() => { if (productImages.value.length === 0) { return null } - return productImages.value[currentImageIndex.value] + return thumbnail(productImages.value[currentImageIndex.value], 400) }) // Image cycling methods diff --git a/src/modules/market/views/CheckoutPage.vue b/src/modules/market/views/CheckoutPage.vue index a5cbdb7..122d179 100644 --- a/src/modules/market/views/CheckoutPage.vue +++ b/src/modules/market/views/CheckoutPage.vue @@ -56,7 +56,7 @@