@wix/web5-core 1.63.20 → 1.63.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/ui/OptimizedImage.js +30 -12
- package/dist/cjs/components/ui/OptimizedImage.js.map +1 -1
- package/dist/cjs/context/ImageSetContext.js +234 -0
- package/dist/cjs/context/ImageSetContext.js.map +1 -0
- package/dist/cjs/image/imageSetTypes.js +108 -0
- package/dist/cjs/image/imageSetTypes.js.map +1 -0
- package/dist/cjs/index.js +16 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/dependencies.js.map +1 -1
- package/dist/esm/components/ui/OptimizedImage.js +21 -3
- package/dist/esm/components/ui/OptimizedImage.js.map +1 -1
- package/dist/esm/context/ImageSetContext.js +221 -0
- package/dist/esm/context/ImageSetContext.js.map +1 -0
- package/dist/esm/image/imageSetTypes.js +100 -0
- package/dist/esm/image/imageSetTypes.js.map +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/dependencies.js.map +1 -1
- package/dist/types/components/ui/OptimizedImage.d.ts +12 -0
- package/dist/types/components/ui/OptimizedImage.d.ts.map +1 -1
- package/dist/types/context/ImageSetContext.d.ts +74 -0
- package/dist/types/context/ImageSetContext.d.ts.map +1 -0
- package/dist/types/image/imageSetTypes.d.ts +185 -0
- package/dist/types/image/imageSetTypes.d.ts.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types/dependencies.d.ts +15 -0
- package/dist/types/types/dependencies.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -4,8 +4,10 @@ import { loadBackdropAnalysis } from '../../utils/imageBackdrop.js';
|
|
|
4
4
|
import { isWeb5ImageUrl } from '../../types/link-types.js';
|
|
5
5
|
import { useComponentDependencies } from '../../context/ComponentDependenciesContext.js';
|
|
6
6
|
import { useResolvedImageSources } from '../../hooks/useResolvedImageSources.js';
|
|
7
|
+
import { backdropColorOf } from '../../image/imageSetTypes.js';
|
|
7
8
|
const lowResFallbackCache = new Map();
|
|
8
9
|
export const OptimizedImage = /*#__PURE__*/memo(props => {
|
|
10
|
+
var _slot$visualMetadata, _slot$crop;
|
|
9
11
|
const {
|
|
10
12
|
alt,
|
|
11
13
|
className = '',
|
|
@@ -23,6 +25,7 @@ export const OptimizedImage = /*#__PURE__*/memo(props => {
|
|
|
23
25
|
disableResize = false,
|
|
24
26
|
imageMode = 'fit',
|
|
25
27
|
adaptiveBackdrop = false,
|
|
28
|
+
slot,
|
|
26
29
|
fallbackOnLowRes,
|
|
27
30
|
lowResThreshold = 550
|
|
28
31
|
} = props;
|
|
@@ -121,6 +124,21 @@ export const OptimizedImage = /*#__PURE__*/memo(props => {
|
|
|
121
124
|
const effectiveObjectFit = adaptiveActive ? backdrop != null && backdrop.hasBackdrop ? 'contain' : 'cover' : objectFit;
|
|
122
125
|
const effectiveImageMode = adaptiveActive ? backdrop != null && backdrop.hasBackdrop ? 'fit' : 'fill' : imageMode;
|
|
123
126
|
const adaptiveBackgroundColor = adaptiveActive && backdrop != null && backdrop.hasBackdrop ? backdrop.backgroundColor : undefined;
|
|
127
|
+
|
|
128
|
+
// A resolved slot answers the same two questions the canvas probe was asking,
|
|
129
|
+
// from the server's own decode. It wins wherever it is present.
|
|
130
|
+
//
|
|
131
|
+
// `contained` (ADR 0220) forces `contain` regardless of anything else: the
|
|
132
|
+
// server deliberately returned a crop at a different aspect than the slot
|
|
133
|
+
// asked for, so that the whole product stays in frame. Cropping it again with
|
|
134
|
+
// `cover` would undo exactly the cut it went out of its way to avoid, and the
|
|
135
|
+
// padding it leaves behind is what `backgroundColor` is for — guaranteed
|
|
136
|
+
// non-empty in that case.
|
|
137
|
+
const slotBackground = slot == null || (_slot$visualMetadata = slot.visualMetadata) == null ? void 0 : _slot$visualMetadata.background;
|
|
138
|
+
const slotIsSolid = slotBackground === 'IMAGE_BACKGROUND_SOLID';
|
|
139
|
+
const slotWantsContain = Boolean(slot == null || (_slot$crop = slot.crop) == null ? void 0 : _slot$crop.contained) || slotIsSolid;
|
|
140
|
+
const resolvedObjectFit = slot ? slotWantsContain ? 'contain' : 'cover' : effectiveObjectFit;
|
|
141
|
+
const resolvedBackgroundColor = slot ? slotWantsContain ? backdropColorOf(slot) : undefined : adaptiveBackgroundColor;
|
|
124
142
|
useEffect(() => {
|
|
125
143
|
if (!adaptiveActive) {
|
|
126
144
|
return;
|
|
@@ -339,7 +357,7 @@ export const OptimizedImage = /*#__PURE__*/memo(props => {
|
|
|
339
357
|
className: ['block', layout === 'intrinsic' ? '' : 'relative overflow-hidden', showDynamicBadge ? 'relative' : '', className].filter(Boolean).join(' '),
|
|
340
358
|
style: layout === 'intrinsic' ? undefined : {
|
|
341
359
|
minHeight: '1px',
|
|
342
|
-
backgroundColor:
|
|
360
|
+
backgroundColor: resolvedBackgroundColor
|
|
343
361
|
},
|
|
344
362
|
onClick: clickable ? onClick : undefined,
|
|
345
363
|
onKeyDown: clickable ? handleKeyDown : undefined,
|
|
@@ -376,7 +394,7 @@ export const OptimizedImage = /*#__PURE__*/memo(props => {
|
|
|
376
394
|
}, "Failed to load image"))), imageUrl && !isError && /*#__PURE__*/React.createElement("img", {
|
|
377
395
|
src: imageUrl,
|
|
378
396
|
alt: alt,
|
|
379
|
-
className: `${layout === 'intrinsic' ? 'w-full h-auto' : 'absolute inset-0 w-full h-full'} transition-opacity duration-300 ${isLoaded ? 'opacity-100' : 'opacity-0'} ${
|
|
397
|
+
className: `${layout === 'intrinsic' ? 'w-full h-auto' : 'absolute inset-0 w-full h-full'} transition-opacity duration-300 ${isLoaded ? 'opacity-100' : 'opacity-0'} ${resolvedObjectFit === 'contain' ? 'object-contain' : resolvedObjectFit === 'cover' ? 'object-cover' : resolvedObjectFit === 'fill' ? 'object-fill' : resolvedObjectFit === 'none' ? 'object-none' : resolvedObjectFit === 'scale-down' ? 'object-scale-down' : ''}`,
|
|
380
398
|
style: layout === 'intrinsic' ? {
|
|
381
399
|
width: '100%',
|
|
382
400
|
height: 'auto',
|
|
@@ -387,7 +405,7 @@ export const OptimizedImage = /*#__PURE__*/memo(props => {
|
|
|
387
405
|
inset: 0,
|
|
388
406
|
width: '100%',
|
|
389
407
|
height: '100%',
|
|
390
|
-
objectFit:
|
|
408
|
+
objectFit: resolvedObjectFit,
|
|
391
409
|
maxWidth: '100%',
|
|
392
410
|
display: 'block'
|
|
393
411
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","useState","useRef","useEffect","memo","useMemo","getResizedImageUrl","normalizeImageUrl","loadBackdropAnalysis","isWeb5ImageUrl","useComponentDependencies","useResolvedImageSources","lowResFallbackCache","Map","OptimizedImage","props","alt","className","quality","format","progressive","placeholderColor","objectFit","layout","onLoad","onError","onClick","clickable","imageFromText","disableResize","imageMode","adaptiveBackdrop","fallbackOnLowRes","lowResThreshold","isResolvedMode","unresolvedSource","filename","preResolvedUrl","resolvedUrl","filter","undefined","sourceIdentifier","deps","isLoaded","setIsLoaded","isError","setIsError","imageUrl","setImageUrl","isFallback","setIsFallback","backdrop","setBackdrop","isDebugImageEnabled","useDebugImageContext","dimensions","setDimensions","containerRef","observerRef","isContextualImage","contextualImageKey","rawKey","slice","length","decodeURIComponent","trim","resolutionInputs","src","images","resolvedSources","loading","isResolutionLoading","resolvedSource","logContextualImage","url","contextualTerm","imageEventType","isImageResolved","isFallbackImage","contextualImageFallback","fallbacks","fallbackImages","Math","floor","random","effectiveFilename","sourceCacheKey","hasResolvedSourceDimensions","width","height","isLowResResolvedAsset","cachedFallbackUrl","get","showDynamicBadge","adaptiveActive","adaptiveResolved","effectiveObjectFit","hasBackdrop","effectiveImageMode","adaptiveBackgroundColor","backgroundColor","token","aborted","then","result","set","container","current","updateDimensions","rect","getBoundingClientRect","ceil","window","ResizeObserver","observe","handleResize","addEventListener","disconnect","removeEventListener","normalizedUrl","isExternalNonWixUrl","startsWith","includes","imageFilename","match","replace","dpr","devicePixelRatio","optimizedUrl","handleImageLoad","e","img","currentTarget","naturalWidth","naturalHeight","originalImageUrl","currentSrc","console","warn","fallbackImageUrl","handleImageError","error","source","Error","isTransparentPlaceholder","placeholderStyles","background","animation","backgroundSize","handleKeyDown","key","preventDefault","createElement","Fragment","ref","Boolean","join","style","minHeight","onKeyDown","tabIndex","role","fill","stroke","viewBox","focusable","strokeLinecap","strokeLinejoin","strokeWidth","d","maxWidth","display","position","inset"],"sources":["../../../../src/components/ui/OptimizedImage.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect, memo, useMemo } from 'react';\nimport { getResizedImageUrl, normalizeImageUrl } from '../../utils/image-utils';\nimport {\n loadBackdropAnalysis,\n type BackdropAnalysis,\n} from '../../utils/imageBackdrop';\nimport { isWeb5ImageUrl } from '../../types/link-types';\nimport { useComponentDependencies } from '../../context/ComponentDependenciesContext';\nimport type { ImageSearchFilterString } from '../../image/imageSearchFilterTypes';\nimport { useResolvedImageSources } from '../../hooks/useResolvedImageSources';\n\nconst lowResFallbackCache = new Map<string, string>();\n\ninterface OptimizedImageBaseProps {\n /** Accessible alternative text for the rendered image. */\n alt: string;\n /** Additional CSS classes applied to the outer image container. */\n className?: string;\n /** Preferred output quality for resized Wix media URLs. */\n quality?: number;\n /** Requested output format for future image delivery customization. */\n format?: 'auto' | 'webp' | 'jpg' | 'png';\n /** Enables progressive loading behavior when supported by the delivery layer. */\n progressive?: boolean;\n /** Placeholder background color shown before the image loads. */\n placeholderColor?: string;\n /** CSS object-fit behavior applied to the rendered `<img>`. */\n objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';\n /** Layout strategy for the image container. */\n layout?: 'fill' | 'intrinsic';\n /** Called after the final rendered image successfully loads. */\n onLoad?: () => void;\n /** Called when the final rendered image fails to load. */\n onError?: (error: Error) => void;\n /** Click handler for interactive image surfaces. */\n onClick?: () => void;\n /** Makes the image container keyboard-focusable and clickable. */\n clickable?: boolean;\n /** Marks the source as originating from text-driven image generation for BI logging. */\n imageFromText?: boolean;\n /** Skips Wix fitted delivery URL rewriting and uses the normalized original URL directly. */\n disableResize?: boolean;\n /** CDN resize mode. 'fill' crops to exact container dimensions (sharp at slot size).\n * 'fit' (default) preserves aspect ratio. */\n imageMode?: 'fit' | 'fill';\n /**\n * Auto-pick `objectFit`/`imageMode` per image by sniffing its edges on a\n * canvas (see `imageBackdrop.ts`). Images shot on a seamless backdrop are\n * rendered `contain` with the slot painted that backdrop colour (the subject\n * is never cropped, the letterbox is invisible); full-bleed scenes fall back\n * to `cover`. Overrides `objectFit`/`imageMode` once analysis resolves; until\n * then, and on any probe/CORS failure, the passed `objectFit`/`imageMode`\n * apply. No effect for `layout=\"intrinsic\"`. */\n adaptiveBackdrop?: boolean;\n /** Full URL to a fallback image used when the primary image is considered low resolution. */\n fallbackOnLowRes?: string;\n /** Minimum acceptable width or height before low-resolution fallback is triggered. */\n lowResThreshold?: number;\n}\n\ninterface OptimizedImageWithFilenameProps {\n /** Image source to render. Supports full URLs and `web5://image/...` contextual image tokens. */\n filename: string;\n /** Optional contextual search filter used only when resolving a `web5://image/...` source. */\n filter?: ImageSearchFilterString;\n /** Mutually exclusive with `filename`; provide this only when the image URL is already resolved. */\n resolvedUrl?: never;\n}\n\ninterface OptimizedImageWithResolvedUrlProps {\n /** Already-resolved image URL. When provided, OptimizedImage skips all source resolution. */\n resolvedUrl: string;\n /** Mutually exclusive with `resolvedUrl`; use `filename` when the source still needs resolving. */\n filename?: never;\n /** Not applicable for already-resolved image URLs. */\n filter?: never;\n}\n\nexport type OptimizedImageProps = OptimizedImageBaseProps &\n (OptimizedImageWithFilenameProps | OptimizedImageWithResolvedUrlProps);\n\nexport const OptimizedImage: React.FC<OptimizedImageProps> = memo((props) => {\n const {\n alt,\n className = '',\n quality = 85,\n format = 'auto',\n progressive = true,\n placeholderColor = '#f3f4f6',\n objectFit = 'contain',\n layout = 'fill',\n onLoad,\n onError,\n onClick,\n clickable = false,\n imageFromText = false,\n disableResize = false,\n imageMode = 'fit',\n adaptiveBackdrop = false,\n fallbackOnLowRes,\n lowResThreshold = 550,\n } = props;\n void format;\n void progressive;\n\n const isResolvedMode = 'resolvedUrl' in props;\n const unresolvedSource = 'filename' in props ? props.filename ?? '' : '';\n const preResolvedUrl = 'resolvedUrl' in props ? props.resolvedUrl : '';\n const filter = 'filename' in props ? props.filter : undefined;\n const sourceIdentifier = isResolvedMode ? preResolvedUrl : unresolvedSource;\n\n const deps = useComponentDependencies();\n const [isLoaded, setIsLoaded] = useState(false);\n const [isError, setIsError] = useState(false);\n const [imageUrl, setImageUrl] = useState<string>('');\n const [isFallback, setIsFallback] = useState(false);\n // null = adaptive analysis pending (only meaningful when adaptiveBackdrop).\n const [backdrop, setBackdrop] = useState<BackdropAnalysis | null>(null);\n const isDebugImageEnabled = deps.useDebugImageContext();\n const [dimensions, setDimensions] = useState<{\n width: number;\n height: number;\n } | null>(null);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const observerRef = useRef<ResizeObserver | null>(null);\n\n const isContextualImage = useMemo(\n () =>\n !isResolvedMode &&\n !!(unresolvedSource && isWeb5ImageUrl(unresolvedSource)),\n [isResolvedMode, unresolvedSource],\n );\n\n const contextualImageKey = useMemo(() => {\n if (!isContextualImage) {\n return '';\n }\n const rawKey = unresolvedSource.slice('web5://image/'.length);\n try {\n return decodeURIComponent(rawKey).trim();\n } catch {\n return rawKey.trim();\n }\n }, [isContextualImage, unresolvedSource]);\n const resolutionInputs = useMemo(\n () =>\n isResolvedMode || !unresolvedSource\n ? []\n : [\n {\n src: unresolvedSource,\n filter,\n },\n ],\n [filter, isResolvedMode, unresolvedSource],\n );\n const { images: resolvedSources, loading: isResolutionLoading } =\n useResolvedImageSources(resolutionInputs);\n const resolvedSource = useMemo(\n () => (!isResolvedMode ? resolvedSources?.[0] ?? null : null),\n [isResolvedMode, resolvedSources],\n );\n\n useEffect(() => {\n if (\n !isContextualImage ||\n isResolvedMode ||\n isResolutionLoading ||\n resolvedSources === null\n ) {\n return;\n }\n\n deps.logContextualImage?.({\n imageUrl: resolvedSource?.url ?? '',\n contextualTerm: contextualImageKey,\n imageEventType: imageFromText\n ? 'web5_image_link_from_text'\n : 'web5_image_link',\n isImageResolved: !!resolvedSource?.url,\n isFallbackImage: false,\n });\n }, [\n contextualImageKey,\n deps,\n imageFromText,\n isContextualImage,\n isResolvedMode,\n isResolutionLoading,\n resolvedSource?.url,\n resolvedSources,\n ]);\n\n // When imageFromText contextual image resolution returns empty, use a random fallback\n const contextualImageFallback = useMemo(() => {\n if (\n !imageFromText ||\n !isContextualImage ||\n isResolutionLoading ||\n resolvedSources === null ||\n resolvedSource?.url\n ) {\n return '';\n }\n const fallbacks = deps.fallbackImages;\n if (!fallbacks || fallbacks.length === 0) {\n return '';\n }\n return fallbacks[Math.floor(Math.random() * fallbacks.length)];\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n imageFromText,\n isContextualImage,\n isResolutionLoading,\n resolvedSources,\n resolvedSource?.url,\n deps.fallbackImages,\n ]);\n\n // Log when a fallback image is used for an unresolved contextual image\n useEffect(() => {\n if (!contextualImageFallback) {\n return;\n }\n deps.logContextualImage?.({\n imageUrl: contextualImageFallback,\n contextualTerm: contextualImageKey,\n imageEventType: 'web5_image_fallback',\n isImageResolved: false,\n isFallbackImage: true,\n });\n }, [contextualImageFallback, contextualImageKey, deps]);\n\n const effectiveFilename = isResolvedMode\n ? preResolvedUrl\n : resolvedSource?.url || contextualImageFallback;\n const sourceCacheKey = useMemo(\n () =>\n effectiveFilename ? normalizeImageUrl(effectiveFilename).trim() : '',\n [effectiveFilename],\n );\n const hasResolvedSourceDimensions =\n typeof resolvedSource?.width === 'number' &&\n typeof resolvedSource?.height === 'number';\n const isLowResResolvedAsset =\n hasResolvedSourceDimensions &&\n (resolvedSource.width! < lowResThreshold ||\n resolvedSource.height! < lowResThreshold);\n const cachedFallbackUrl = useMemo(\n () => (sourceCacheKey ? lowResFallbackCache.get(sourceCacheKey) ?? '' : ''),\n [sourceCacheKey],\n );\n const showDynamicBadge = isContextualImage && isDebugImageEnabled;\n\n // Adaptive fit: probe the resolved image's edges, then map to fit/object-fit.\n // Until analysis resolves (`backdrop === null`) the displayed URL is held back\n // so we never fetch with the wrong mode and flash a re-crop. On a backdrop we\n // render `contain` (whole subject visible) and paint the slot its colour; on a\n // busy scene we `cover`. Failures resolve to `hasBackdrop: false` → `cover`.\n const adaptiveActive = adaptiveBackdrop && layout !== 'intrinsic';\n const adaptiveResolved = !adaptiveActive || backdrop !== null;\n const effectiveObjectFit = adaptiveActive\n ? backdrop?.hasBackdrop\n ? 'contain'\n : 'cover'\n : objectFit;\n const effectiveImageMode = adaptiveActive\n ? backdrop?.hasBackdrop\n ? 'fit'\n : 'fill'\n : imageMode;\n const adaptiveBackgroundColor =\n adaptiveActive && backdrop?.hasBackdrop\n ? backdrop.backgroundColor\n : undefined;\n\n useEffect(() => {\n if (!adaptiveActive) {\n return;\n }\n setBackdrop(null);\n if (\n !effectiveFilename ||\n isFallback ||\n cachedFallbackUrl ||\n (isLowResResolvedAsset && fallbackOnLowRes)\n ) {\n // No analyzable primary source → behave like a busy scene (cover).\n setBackdrop({ hasBackdrop: false });\n return;\n }\n const token = { aborted: false };\n loadBackdropAnalysis(effectiveFilename, token).then((result) => {\n if (!token.aborted) {\n setBackdrop(result);\n }\n });\n return () => {\n token.aborted = true;\n };\n }, [\n adaptiveActive,\n effectiveFilename,\n isFallback,\n cachedFallbackUrl,\n isLowResResolvedAsset,\n fallbackOnLowRes,\n ]);\n\n useEffect(() => {\n setIsLoaded(false);\n setIsError(false);\n setIsFallback(false);\n setImageUrl('');\n }, [sourceCacheKey, fallbackOnLowRes]);\n\n useEffect(() => {\n if (!sourceCacheKey || !fallbackOnLowRes) {\n return;\n }\n\n if (isLowResResolvedAsset) {\n lowResFallbackCache.set(sourceCacheKey, fallbackOnLowRes);\n setIsFallback(true);\n setImageUrl(fallbackOnLowRes);\n return;\n }\n\n if (!cachedFallbackUrl) {\n return;\n }\n\n setIsFallback(true);\n setImageUrl(cachedFallbackUrl);\n }, [\n cachedFallbackUrl,\n fallbackOnLowRes,\n isLowResResolvedAsset,\n sourceCacheKey,\n ]);\n\n // For fill layout, measure container so we can request a resized Wix image.\n useEffect(() => {\n if (layout === 'intrinsic' || disableResize) {\n return;\n }\n const container = containerRef.current;\n if (!container) {\n return;\n }\n\n const updateDimensions = () => {\n const rect = container.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n setDimensions({\n width: Math.ceil(rect.width),\n height: Math.ceil(rect.height),\n });\n }\n };\n\n updateDimensions();\n\n if ('ResizeObserver' in window) {\n observerRef.current = new ResizeObserver(() => {\n updateDimensions();\n });\n observerRef.current.observe(container);\n }\n\n const handleResize = () => updateDimensions();\n window.addEventListener('resize', handleResize);\n\n return () => {\n if (observerRef.current) {\n observerRef.current.disconnect();\n }\n window.removeEventListener('resize', handleResize);\n };\n }, [disableResize, layout]);\n\n // Intrinsic layout: use normalized URL without resize logic.\n useEffect(() => {\n if (layout !== 'intrinsic') {\n return;\n }\n if (\n !effectiveFilename ||\n isFallback ||\n cachedFallbackUrl ||\n (isLowResResolvedAsset && fallbackOnLowRes)\n ) {\n return;\n }\n\n const normalizedUrl = normalizeImageUrl(effectiveFilename);\n setImageUrl(normalizedUrl);\n }, [\n cachedFallbackUrl,\n effectiveFilename,\n isFallback,\n isLowResResolvedAsset,\n layout,\n ]);\n\n // External non-Wix URLs skip resizing; use them immediately.\n useEffect(() => {\n if (layout === 'intrinsic') {\n return;\n }\n if (\n !effectiveFilename ||\n isFallback ||\n cachedFallbackUrl ||\n (isLowResResolvedAsset && fallbackOnLowRes)\n ) {\n return;\n }\n\n const normalizedUrl = normalizeImageUrl(effectiveFilename);\n const isExternalNonWixUrl =\n (normalizedUrl.startsWith('http://') ||\n normalizedUrl.startsWith('https://')) &&\n !normalizedUrl.includes('static.wixstatic.com');\n\n if (isExternalNonWixUrl) {\n setImageUrl(normalizedUrl);\n }\n }, [\n cachedFallbackUrl,\n effectiveFilename,\n isFallback,\n isLowResResolvedAsset,\n layout,\n ]);\n\n // Explicit opt-out: use original normalized URL even in fill layouts.\n useEffect(() => {\n if (layout === 'intrinsic') {\n return;\n }\n if (\n !disableResize ||\n !effectiveFilename ||\n isFallback ||\n cachedFallbackUrl ||\n (isLowResResolvedAsset && fallbackOnLowRes)\n ) {\n return;\n }\n\n setImageUrl(normalizeImageUrl(effectiveFilename));\n }, [\n cachedFallbackUrl,\n disableResize,\n effectiveFilename,\n isFallback,\n isLowResResolvedAsset,\n layout,\n ]);\n\n // Wix/local URLs: wait for dimensions, then build a resized URL.\n useEffect(() => {\n if (layout === 'intrinsic') {\n return;\n }\n if (disableResize) {\n return;\n }\n // Hold the fetch until adaptive analysis picks fit vs fill, so we request\n // the right CDN mode once instead of fetching twice and re-cropping.\n if (!adaptiveResolved) {\n return;\n }\n if (\n !dimensions ||\n !effectiveFilename ||\n isFallback ||\n cachedFallbackUrl ||\n (isLowResResolvedAsset && fallbackOnLowRes)\n ) {\n return;\n }\n\n const normalizedUrl = normalizeImageUrl(effectiveFilename);\n const isExternalNonWixUrl =\n (normalizedUrl.startsWith('http://') ||\n normalizedUrl.startsWith('https://')) &&\n !normalizedUrl.includes('static.wixstatic.com');\n\n if (isExternalNonWixUrl) {\n return;\n }\n\n let imageFilename = effectiveFilename;\n if (normalizedUrl.includes('static.wixstatic.com')) {\n const match = normalizedUrl.match(/media\\/([^/?]+)/);\n if (match && match[1]) {\n imageFilename = match[1];\n } else {\n setImageUrl(normalizedUrl);\n return;\n }\n } else if (\n normalizedUrl.startsWith('https://static.wixstatic.com/media/')\n ) {\n imageFilename = normalizedUrl.replace(\n 'https://static.wixstatic.com/media/',\n '',\n );\n }\n\n const dpr = window.devicePixelRatio ?? 1;\n const optimizedUrl = getResizedImageUrl(\n imageFilename,\n dimensions.width * dpr,\n dimensions.height * dpr,\n { quality, imageMode: effectiveImageMode },\n );\n setImageUrl(optimizedUrl);\n }, [\n dimensions,\n cachedFallbackUrl,\n effectiveFilename,\n isFallback,\n isLowResResolvedAsset,\n layout,\n disableResize,\n quality,\n effectiveImageMode,\n adaptiveResolved,\n ]);\n\n const handleImageLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {\n const img = e.currentTarget;\n\n if (\n fallbackOnLowRes &&\n !isFallback &&\n !hasResolvedSourceDimensions &&\n (img.naturalWidth < lowResThreshold ||\n img.naturalHeight < lowResThreshold)\n ) {\n const originalImageUrl =\n img.currentSrc || imageUrl || effectiveFilename || sourceIdentifier;\n\n if (sourceCacheKey) {\n lowResFallbackCache.set(sourceCacheKey, fallbackOnLowRes);\n }\n\n setIsFallback(true);\n setImageUrl(fallbackOnLowRes);\n setIsLoaded(false);\n\n console.warn('OptimizedImage applied a low-res fallback image.', {\n originalImageUrl,\n fallbackImageUrl: fallbackOnLowRes,\n naturalWidth: img.naturalWidth,\n naturalHeight: img.naturalHeight,\n lowResThreshold,\n });\n\n deps.logContextualImage?.({\n imageUrl: fallbackOnLowRes,\n contextualTerm: contextualImageKey,\n imageEventType: 'web5_image_fallback',\n isImageResolved: true,\n isFallbackImage: true,\n });\n return;\n }\n\n setIsLoaded(true);\n onLoad?.();\n };\n\n const handleImageError = () => {\n setIsError(true);\n console.error('Failed to load image:', {\n source: sourceIdentifier,\n imageUrl,\n alt,\n });\n onError?.(new Error(`Failed to load image: ${sourceIdentifier}`));\n };\n\n const isTransparentPlaceholder = placeholderColor === 'transparent';\n const placeholderStyles = isTransparentPlaceholder\n ? {\n background: 'transparent',\n animation: 'none',\n }\n : {\n background: `linear-gradient(90deg, ${placeholderColor} 0%, #e5e7eb 50%, ${placeholderColor} 100%)`,\n backgroundSize: '200% 100%',\n animation: 'shimmer 2s infinite ease-in-out',\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (onClick && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onClick();\n }\n };\n\n return (\n <>\n <span\n ref={containerRef}\n className={[\n 'block',\n layout === 'intrinsic' ? '' : 'relative overflow-hidden',\n showDynamicBadge ? 'relative' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={\n layout === 'intrinsic'\n ? undefined\n : { minHeight: '1px', backgroundColor: adaptiveBackgroundColor }\n }\n onClick={clickable ? onClick : undefined}\n onKeyDown={clickable ? handleKeyDown : undefined}\n tabIndex={clickable ? 0 : undefined}\n role={clickable ? 'button' : undefined}\n aria-label={clickable ? alt : undefined}\n >\n {showDynamicBadge && (\n <span className=\"absolute left-2 top-2 z-10 rounded bg-blue-600/90 px-2 py-1 text-[10px] font-semibold tracking-wide text-white shadow block\">\n {contextualImageKey}\n </span>\n )}\n {!isLoaded && !isError && sourceIdentifier && (\n <span\n className=\"absolute inset-0 rounded block\"\n style={placeholderStyles}\n role=\"status\"\n aria-label=\"Loading image\"\n />\n )}\n\n {isError && (\n <span\n className=\"absolute inset-0 flex items-center justify-center bg-gray-100 text-gray-500 rounded block\"\n role=\"alert\"\n aria-live=\"assertive\"\n >\n <span className=\"text-center p-4 block\">\n <svg\n className=\"w-8 h-8 mx-auto mb-2 text-gray-400\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n <span className=\"text-xs block\">Failed to load image</span>\n </span>\n </span>\n )}\n\n {imageUrl && !isError && (\n <img\n src={imageUrl}\n alt={alt}\n className={`${\n layout === 'intrinsic'\n ? 'w-full h-auto'\n : 'absolute inset-0 w-full h-full'\n } transition-opacity duration-300 ${\n isLoaded ? 'opacity-100' : 'opacity-0'\n } ${\n effectiveObjectFit === 'contain'\n ? 'object-contain'\n : effectiveObjectFit === 'cover'\n ? 'object-cover'\n : effectiveObjectFit === 'fill'\n ? 'object-fill'\n : effectiveObjectFit === 'none'\n ? 'object-none'\n : effectiveObjectFit === 'scale-down'\n ? 'object-scale-down'\n : ''\n }`}\n style={\n layout === 'intrinsic'\n ? {\n width: '100%',\n height: 'auto',\n maxWidth: '100%',\n display: 'block',\n }\n : {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n objectFit: effectiveObjectFit,\n maxWidth: '100%',\n display: 'block',\n }\n }\n onLoad={handleImageLoad}\n onError={handleImageError}\n loading={imageUrl.startsWith('data:') ? 'eager' : 'lazy'}\n />\n )}\n </span>\n </>\n );\n});\n"],"mappings":"AAAA,OAAOA,KAAK,IAAIC,QAAQ,EAAEC,MAAM,EAAEC,SAAS,EAAEC,IAAI,EAAEC,OAAO,QAAQ,OAAO;AACzE,SAASC,kBAAkB,EAAEC,iBAAiB,QAAQ,yBAAyB;AAC/E,SACEC,oBAAoB,QAEf,2BAA2B;AAClC,SAASC,cAAc,QAAQ,wBAAwB;AACvD,SAASC,wBAAwB,QAAQ,4CAA4C;AAErF,SAASC,uBAAuB,QAAQ,qCAAqC;AAE7E,MAAMC,mBAAmB,GAAG,IAAIC,GAAG,CAAiB,CAAC;AAsErD,OAAO,MAAMC,cAA6C,gBAAGV,IAAI,CAAEW,KAAK,IAAK;EAC3E,MAAM;IACJC,GAAG;IACHC,SAAS,GAAG,EAAE;IACdC,OAAO,GAAG,EAAE;IACZC,MAAM,GAAG,MAAM;IACfC,WAAW,GAAG,IAAI;IAClBC,gBAAgB,GAAG,SAAS;IAC5BC,SAAS,GAAG,SAAS;IACrBC,MAAM,GAAG,MAAM;IACfC,MAAM;IACNC,OAAO;IACPC,OAAO;IACPC,SAAS,GAAG,KAAK;IACjBC,aAAa,GAAG,KAAK;IACrBC,aAAa,GAAG,KAAK;IACrBC,SAAS,GAAG,KAAK;IACjBC,gBAAgB,GAAG,KAAK;IACxBC,gBAAgB;IAChBC,eAAe,GAAG;EACpB,CAAC,GAAGlB,KAAK;EACT,KAAKI,MAAM;EACX,KAAKC,WAAW;EAEhB,MAAMc,cAAc,GAAG,aAAa,IAAInB,KAAK;EAC7C,MAAMoB,gBAAgB,GAAG,UAAU,IAAIpB,KAAK,GAAGA,KAAK,CAACqB,QAAQ,IAAI,EAAE,GAAG,EAAE;EACxE,MAAMC,cAAc,GAAG,aAAa,IAAItB,KAAK,GAAGA,KAAK,CAACuB,WAAW,GAAG,EAAE;EACtE,MAAMC,MAAM,GAAG,UAAU,IAAIxB,KAAK,GAAGA,KAAK,CAACwB,MAAM,GAAGC,SAAS;EAC7D,MAAMC,gBAAgB,GAAGP,cAAc,GAAGG,cAAc,GAAGF,gBAAgB;EAE3E,MAAMO,IAAI,GAAGhC,wBAAwB,CAAC,CAAC;EACvC,MAAM,CAACiC,QAAQ,EAAEC,WAAW,CAAC,GAAG3C,QAAQ,CAAC,KAAK,CAAC;EAC/C,MAAM,CAAC4C,OAAO,EAAEC,UAAU,CAAC,GAAG7C,QAAQ,CAAC,KAAK,CAAC;EAC7C,MAAM,CAAC8C,QAAQ,EAAEC,WAAW,CAAC,GAAG/C,QAAQ,CAAS,EAAE,CAAC;EACpD,MAAM,CAACgD,UAAU,EAAEC,aAAa,CAAC,GAAGjD,QAAQ,CAAC,KAAK,CAAC;EACnD;EACA,MAAM,CAACkD,QAAQ,EAAEC,WAAW,CAAC,GAAGnD,QAAQ,CAA0B,IAAI,CAAC;EACvE,MAAMoD,mBAAmB,GAAGX,IAAI,CAACY,oBAAoB,CAAC,CAAC;EACvD,MAAM,CAACC,UAAU,EAAEC,aAAa,CAAC,GAAGvD,QAAQ,CAGlC,IAAI,CAAC;EAEf,MAAMwD,YAAY,GAAGvD,MAAM,CAAiB,IAAI,CAAC;EACjD,MAAMwD,WAAW,GAAGxD,MAAM,CAAwB,IAAI,CAAC;EAEvD,MAAMyD,iBAAiB,GAAGtD,OAAO,CAC/B,MACE,CAAC6B,cAAc,IACf,CAAC,EAAEC,gBAAgB,IAAI1B,cAAc,CAAC0B,gBAAgB,CAAC,CAAC,EAC1D,CAACD,cAAc,EAAEC,gBAAgB,CACnC,CAAC;EAED,MAAMyB,kBAAkB,GAAGvD,OAAO,CAAC,MAAM;IACvC,IAAI,CAACsD,iBAAiB,EAAE;MACtB,OAAO,EAAE;IACX;IACA,MAAME,MAAM,GAAG1B,gBAAgB,CAAC2B,KAAK,CAAC,eAAe,CAACC,MAAM,CAAC;IAC7D,IAAI;MACF,OAAOC,kBAAkB,CAACH,MAAM,CAAC,CAACI,IAAI,CAAC,CAAC;IAC1C,CAAC,CAAC,MAAM;MACN,OAAOJ,MAAM,CAACI,IAAI,CAAC,CAAC;IACtB;EACF,CAAC,EAAE,CAACN,iBAAiB,EAAExB,gBAAgB,CAAC,CAAC;EACzC,MAAM+B,gBAAgB,GAAG7D,OAAO,CAC9B,MACE6B,cAAc,IAAI,CAACC,gBAAgB,GAC/B,EAAE,GACF,CACE;IACEgC,GAAG,EAAEhC,gBAAgB;IACrBI;EACF,CAAC,CACF,EACP,CAACA,MAAM,EAAEL,cAAc,EAAEC,gBAAgB,CAC3C,CAAC;EACD,MAAM;IAAEiC,MAAM,EAAEC,eAAe;IAAEC,OAAO,EAAEC;EAAoB,CAAC,GAC7D5D,uBAAuB,CAACuD,gBAAgB,CAAC;EAC3C,MAAMM,cAAc,GAAGnE,OAAO,CAC5B,MAAO,CAAC6B,cAAc,GAAG,CAAAmC,eAAe,oBAAfA,eAAe,CAAG,CAAC,CAAC,KAAI,IAAI,GAAG,IAAK,EAC7D,CAACnC,cAAc,EAAEmC,eAAe,CAClC,CAAC;EAEDlE,SAAS,CAAC,MAAM;IACd,IACE,CAACwD,iBAAiB,IAClBzB,cAAc,IACdqC,mBAAmB,IACnBF,eAAe,KAAK,IAAI,EACxB;MACA;IACF;IAEA3B,IAAI,CAAC+B,kBAAkB,YAAvB/B,IAAI,CAAC+B,kBAAkB,CAAG;MACxB1B,QAAQ,EAAE,CAAAyB,cAAc,oBAAdA,cAAc,CAAEE,GAAG,KAAI,EAAE;MACnCC,cAAc,EAAEf,kBAAkB;MAClCgB,cAAc,EAAEhD,aAAa,GACzB,2BAA2B,GAC3B,iBAAiB;MACrBiD,eAAe,EAAE,CAAC,EAACL,cAAc,YAAdA,cAAc,CAAEE,GAAG;MACtCI,eAAe,EAAE;IACnB,CAAC,CAAC;EACJ,CAAC,EAAE,CACDlB,kBAAkB,EAClBlB,IAAI,EACJd,aAAa,EACb+B,iBAAiB,EACjBzB,cAAc,EACdqC,mBAAmB,EACnBC,cAAc,oBAAdA,cAAc,CAAEE,GAAG,EACnBL,eAAe,CAChB,CAAC;;EAEF;EACA,MAAMU,uBAAuB,GAAG1E,OAAO,CAAC,MAAM;IAC5C,IACE,CAACuB,aAAa,IACd,CAAC+B,iBAAiB,IAClBY,mBAAmB,IACnBF,eAAe,KAAK,IAAI,IACxBG,cAAc,YAAdA,cAAc,CAAEE,GAAG,EACnB;MACA,OAAO,EAAE;IACX;IACA,MAAMM,SAAS,GAAGtC,IAAI,CAACuC,cAAc;IACrC,IAAI,CAACD,SAAS,IAAIA,SAAS,CAACjB,MAAM,KAAK,CAAC,EAAE;MACxC,OAAO,EAAE;IACX;IACA,OAAOiB,SAAS,CAACE,IAAI,CAACC,KAAK,CAACD,IAAI,CAACE,MAAM,CAAC,CAAC,GAAGJ,SAAS,CAACjB,MAAM,CAAC,CAAC;IAC9D;EACF,CAAC,EAAE,CACDnC,aAAa,EACb+B,iBAAiB,EACjBY,mBAAmB,EACnBF,eAAe,EACfG,cAAc,oBAAdA,cAAc,CAAEE,GAAG,EACnBhC,IAAI,CAACuC,cAAc,CACpB,CAAC;;EAEF;EACA9E,SAAS,CAAC,MAAM;IACd,IAAI,CAAC4E,uBAAuB,EAAE;MAC5B;IACF;IACArC,IAAI,CAAC+B,kBAAkB,YAAvB/B,IAAI,CAAC+B,kBAAkB,CAAG;MACxB1B,QAAQ,EAAEgC,uBAAuB;MACjCJ,cAAc,EAAEf,kBAAkB;MAClCgB,cAAc,EAAE,qBAAqB;MACrCC,eAAe,EAAE,KAAK;MACtBC,eAAe,EAAE;IACnB,CAAC,CAAC;EACJ,CAAC,EAAE,CAACC,uBAAuB,EAAEnB,kBAAkB,EAAElB,IAAI,CAAC,CAAC;EAEvD,MAAM2C,iBAAiB,GAAGnD,cAAc,GACpCG,cAAc,GACd,CAAAmC,cAAc,oBAAdA,cAAc,CAAEE,GAAG,KAAIK,uBAAuB;EAClD,MAAMO,cAAc,GAAGjF,OAAO,CAC5B,MACEgF,iBAAiB,GAAG9E,iBAAiB,CAAC8E,iBAAiB,CAAC,CAACpB,IAAI,CAAC,CAAC,GAAG,EAAE,EACtE,CAACoB,iBAAiB,CACpB,CAAC;EACD,MAAME,2BAA2B,GAC/B,QAAOf,cAAc,oBAAdA,cAAc,CAAEgB,KAAK,MAAK,QAAQ,IACzC,QAAOhB,cAAc,oBAAdA,cAAc,CAAEiB,MAAM,MAAK,QAAQ;EAC5C,MAAMC,qBAAqB,GACzBH,2BAA2B,KAC1Bf,cAAc,CAACgB,KAAK,GAAIvD,eAAe,IACtCuC,cAAc,CAACiB,MAAM,GAAIxD,eAAe,CAAC;EAC7C,MAAM0D,iBAAiB,GAAGtF,OAAO,CAC/B,MAAOiF,cAAc,GAAG1E,mBAAmB,CAACgF,GAAG,CAACN,cAAc,CAAC,IAAI,EAAE,GAAG,EAAG,EAC3E,CAACA,cAAc,CACjB,CAAC;EACD,MAAMO,gBAAgB,GAAGlC,iBAAiB,IAAIN,mBAAmB;;EAEjE;EACA;EACA;EACA;EACA;EACA,MAAMyC,cAAc,GAAG/D,gBAAgB,IAAIR,MAAM,KAAK,WAAW;EACjE,MAAMwE,gBAAgB,GAAG,CAACD,cAAc,IAAI3C,QAAQ,KAAK,IAAI;EAC7D,MAAM6C,kBAAkB,GAAGF,cAAc,GACrC3C,QAAQ,YAARA,QAAQ,CAAE8C,WAAW,GACnB,SAAS,GACT,OAAO,GACT3E,SAAS;EACb,MAAM4E,kBAAkB,GAAGJ,cAAc,GACrC3C,QAAQ,YAARA,QAAQ,CAAE8C,WAAW,GACnB,KAAK,GACL,MAAM,GACRnE,SAAS;EACb,MAAMqE,uBAAuB,GAC3BL,cAAc,IAAI3C,QAAQ,YAARA,QAAQ,CAAE8C,WAAW,GACnC9C,QAAQ,CAACiD,eAAe,GACxB5D,SAAS;EAEfrC,SAAS,CAAC,MAAM;IACd,IAAI,CAAC2F,cAAc,EAAE;MACnB;IACF;IACA1C,WAAW,CAAC,IAAI,CAAC;IACjB,IACE,CAACiC,iBAAiB,IAClBpC,UAAU,IACV0C,iBAAiB,IAChBD,qBAAqB,IAAI1D,gBAAiB,EAC3C;MACA;MACAoB,WAAW,CAAC;QAAE6C,WAAW,EAAE;MAAM,CAAC,CAAC;MACnC;IACF;IACA,MAAMI,KAAK,GAAG;MAAEC,OAAO,EAAE;IAAM,CAAC;IAChC9F,oBAAoB,CAAC6E,iBAAiB,EAAEgB,KAAK,CAAC,CAACE,IAAI,CAAEC,MAAM,IAAK;MAC9D,IAAI,CAACH,KAAK,CAACC,OAAO,EAAE;QAClBlD,WAAW,CAACoD,MAAM,CAAC;MACrB;IACF,CAAC,CAAC;IACF,OAAO,MAAM;MACXH,KAAK,CAACC,OAAO,GAAG,IAAI;IACtB,CAAC;EACH,CAAC,EAAE,CACDR,cAAc,EACdT,iBAAiB,EACjBpC,UAAU,EACV0C,iBAAiB,EACjBD,qBAAqB,EACrB1D,gBAAgB,CACjB,CAAC;EAEF7B,SAAS,CAAC,MAAM;IACdyC,WAAW,CAAC,KAAK,CAAC;IAClBE,UAAU,CAAC,KAAK,CAAC;IACjBI,aAAa,CAAC,KAAK,CAAC;IACpBF,WAAW,CAAC,EAAE,CAAC;EACjB,CAAC,EAAE,CAACsC,cAAc,EAAEtD,gBAAgB,CAAC,CAAC;EAEtC7B,SAAS,CAAC,MAAM;IACd,IAAI,CAACmF,cAAc,IAAI,CAACtD,gBAAgB,EAAE;MACxC;IACF;IAEA,IAAI0D,qBAAqB,EAAE;MACzB9E,mBAAmB,CAAC6F,GAAG,CAACnB,cAAc,EAAEtD,gBAAgB,CAAC;MACzDkB,aAAa,CAAC,IAAI,CAAC;MACnBF,WAAW,CAAChB,gBAAgB,CAAC;MAC7B;IACF;IAEA,IAAI,CAAC2D,iBAAiB,EAAE;MACtB;IACF;IAEAzC,aAAa,CAAC,IAAI,CAAC;IACnBF,WAAW,CAAC2C,iBAAiB,CAAC;EAChC,CAAC,EAAE,CACDA,iBAAiB,EACjB3D,gBAAgB,EAChB0D,qBAAqB,EACrBJ,cAAc,CACf,CAAC;;EAEF;EACAnF,SAAS,CAAC,MAAM;IACd,IAAIoB,MAAM,KAAK,WAAW,IAAIM,aAAa,EAAE;MAC3C;IACF;IACA,MAAM6E,SAAS,GAAGjD,YAAY,CAACkD,OAAO;IACtC,IAAI,CAACD,SAAS,EAAE;MACd;IACF;IAEA,MAAME,gBAAgB,GAAGA,CAAA,KAAM;MAC7B,MAAMC,IAAI,GAAGH,SAAS,CAACI,qBAAqB,CAAC,CAAC;MAC9C,IAAID,IAAI,CAACrB,KAAK,GAAG,CAAC,IAAIqB,IAAI,CAACpB,MAAM,GAAG,CAAC,EAAE;QACrCjC,aAAa,CAAC;UACZgC,KAAK,EAAEN,IAAI,CAAC6B,IAAI,CAACF,IAAI,CAACrB,KAAK,CAAC;UAC5BC,MAAM,EAAEP,IAAI,CAAC6B,IAAI,CAACF,IAAI,CAACpB,MAAM;QAC/B,CAAC,CAAC;MACJ;IACF,CAAC;IAEDmB,gBAAgB,CAAC,CAAC;IAElB,IAAI,gBAAgB,IAAII,MAAM,EAAE;MAC9BtD,WAAW,CAACiD,OAAO,GAAG,IAAIM,cAAc,CAAC,MAAM;QAC7CL,gBAAgB,CAAC,CAAC;MACpB,CAAC,CAAC;MACFlD,WAAW,CAACiD,OAAO,CAACO,OAAO,CAACR,SAAS,CAAC;IACxC;IAEA,MAAMS,YAAY,GAAGA,CAAA,KAAMP,gBAAgB,CAAC,CAAC;IAC7CI,MAAM,CAACI,gBAAgB,CAAC,QAAQ,EAAED,YAAY,CAAC;IAE/C,OAAO,MAAM;MACX,IAAIzD,WAAW,CAACiD,OAAO,EAAE;QACvBjD,WAAW,CAACiD,OAAO,CAACU,UAAU,CAAC,CAAC;MAClC;MACAL,MAAM,CAACM,mBAAmB,CAAC,QAAQ,EAAEH,YAAY,CAAC;IACpD,CAAC;EACH,CAAC,EAAE,CAACtF,aAAa,EAAEN,MAAM,CAAC,CAAC;;EAE3B;EACApB,SAAS,CAAC,MAAM;IACd,IAAIoB,MAAM,KAAK,WAAW,EAAE;MAC1B;IACF;IACA,IACE,CAAC8D,iBAAiB,IAClBpC,UAAU,IACV0C,iBAAiB,IAChBD,qBAAqB,IAAI1D,gBAAiB,EAC3C;MACA;IACF;IAEA,MAAMuF,aAAa,GAAGhH,iBAAiB,CAAC8E,iBAAiB,CAAC;IAC1DrC,WAAW,CAACuE,aAAa,CAAC;EAC5B,CAAC,EAAE,CACD5B,iBAAiB,EACjBN,iBAAiB,EACjBpC,UAAU,EACVyC,qBAAqB,EACrBnE,MAAM,CACP,CAAC;;EAEF;EACApB,SAAS,CAAC,MAAM;IACd,IAAIoB,MAAM,KAAK,WAAW,EAAE;MAC1B;IACF;IACA,IACE,CAAC8D,iBAAiB,IAClBpC,UAAU,IACV0C,iBAAiB,IAChBD,qBAAqB,IAAI1D,gBAAiB,EAC3C;MACA;IACF;IAEA,MAAMuF,aAAa,GAAGhH,iBAAiB,CAAC8E,iBAAiB,CAAC;IAC1D,MAAMmC,mBAAmB,GACvB,CAACD,aAAa,CAACE,UAAU,CAAC,SAAS,CAAC,IAClCF,aAAa,CAACE,UAAU,CAAC,UAAU,CAAC,KACtC,CAACF,aAAa,CAACG,QAAQ,CAAC,sBAAsB,CAAC;IAEjD,IAAIF,mBAAmB,EAAE;MACvBxE,WAAW,CAACuE,aAAa,CAAC;IAC5B;EACF,CAAC,EAAE,CACD5B,iBAAiB,EACjBN,iBAAiB,EACjBpC,UAAU,EACVyC,qBAAqB,EACrBnE,MAAM,CACP,CAAC;;EAEF;EACApB,SAAS,CAAC,MAAM;IACd,IAAIoB,MAAM,KAAK,WAAW,EAAE;MAC1B;IACF;IACA,IACE,CAACM,aAAa,IACd,CAACwD,iBAAiB,IAClBpC,UAAU,IACV0C,iBAAiB,IAChBD,qBAAqB,IAAI1D,gBAAiB,EAC3C;MACA;IACF;IAEAgB,WAAW,CAACzC,iBAAiB,CAAC8E,iBAAiB,CAAC,CAAC;EACnD,CAAC,EAAE,CACDM,iBAAiB,EACjB9D,aAAa,EACbwD,iBAAiB,EACjBpC,UAAU,EACVyC,qBAAqB,EACrBnE,MAAM,CACP,CAAC;;EAEF;EACApB,SAAS,CAAC,MAAM;IACd,IAAIoB,MAAM,KAAK,WAAW,EAAE;MAC1B;IACF;IACA,IAAIM,aAAa,EAAE;MACjB;IACF;IACA;IACA;IACA,IAAI,CAACkE,gBAAgB,EAAE;MACrB;IACF;IACA,IACE,CAACxC,UAAU,IACX,CAAC8B,iBAAiB,IAClBpC,UAAU,IACV0C,iBAAiB,IAChBD,qBAAqB,IAAI1D,gBAAiB,EAC3C;MACA;IACF;IAEA,MAAMuF,aAAa,GAAGhH,iBAAiB,CAAC8E,iBAAiB,CAAC;IAC1D,MAAMmC,mBAAmB,GACvB,CAACD,aAAa,CAACE,UAAU,CAAC,SAAS,CAAC,IAClCF,aAAa,CAACE,UAAU,CAAC,UAAU,CAAC,KACtC,CAACF,aAAa,CAACG,QAAQ,CAAC,sBAAsB,CAAC;IAEjD,IAAIF,mBAAmB,EAAE;MACvB;IACF;IAEA,IAAIG,aAAa,GAAGtC,iBAAiB;IACrC,IAAIkC,aAAa,CAACG,QAAQ,CAAC,sBAAsB,CAAC,EAAE;MAClD,MAAME,KAAK,GAAGL,aAAa,CAACK,KAAK,CAAC,iBAAiB,CAAC;MACpD,IAAIA,KAAK,IAAIA,KAAK,CAAC,CAAC,CAAC,EAAE;QACrBD,aAAa,GAAGC,KAAK,CAAC,CAAC,CAAC;MAC1B,CAAC,MAAM;QACL5E,WAAW,CAACuE,aAAa,CAAC;QAC1B;MACF;IACF,CAAC,MAAM,IACLA,aAAa,CAACE,UAAU,CAAC,qCAAqC,CAAC,EAC/D;MACAE,aAAa,GAAGJ,aAAa,CAACM,OAAO,CACnC,qCAAqC,EACrC,EACF,CAAC;IACH;IAEA,MAAMC,GAAG,GAAGd,MAAM,CAACe,gBAAgB,IAAI,CAAC;IACxC,MAAMC,YAAY,GAAG1H,kBAAkB,CACrCqH,aAAa,EACbpE,UAAU,CAACiC,KAAK,GAAGsC,GAAG,EACtBvE,UAAU,CAACkC,MAAM,GAAGqC,GAAG,EACvB;MAAE5G,OAAO;MAAEY,SAAS,EAAEoE;IAAmB,CAC3C,CAAC;IACDlD,WAAW,CAACgF,YAAY,CAAC;EAC3B,CAAC,EAAE,CACDzE,UAAU,EACVoC,iBAAiB,EACjBN,iBAAiB,EACjBpC,UAAU,EACVyC,qBAAqB,EACrBnE,MAAM,EACNM,aAAa,EACbX,OAAO,EACPgF,kBAAkB,EAClBH,gBAAgB,CACjB,CAAC;EAEF,MAAMkC,eAAe,GAAIC,CAAyC,IAAK;IACrE,MAAMC,GAAG,GAAGD,CAAC,CAACE,aAAa;IAE3B,IACEpG,gBAAgB,IAChB,CAACiB,UAAU,IACX,CAACsC,2BAA2B,KAC3B4C,GAAG,CAACE,YAAY,GAAGpG,eAAe,IACjCkG,GAAG,CAACG,aAAa,GAAGrG,eAAe,CAAC,EACtC;MACA,MAAMsG,gBAAgB,GACpBJ,GAAG,CAACK,UAAU,IAAIzF,QAAQ,IAAIsC,iBAAiB,IAAI5C,gBAAgB;MAErE,IAAI6C,cAAc,EAAE;QAClB1E,mBAAmB,CAAC6F,GAAG,CAACnB,cAAc,EAAEtD,gBAAgB,CAAC;MAC3D;MAEAkB,aAAa,CAAC,IAAI,CAAC;MACnBF,WAAW,CAAChB,gBAAgB,CAAC;MAC7BY,WAAW,CAAC,KAAK,CAAC;MAElB6F,OAAO,CAACC,IAAI,CAAC,kDAAkD,EAAE;QAC/DH,gBAAgB;QAChBI,gBAAgB,EAAE3G,gBAAgB;QAClCqG,YAAY,EAAEF,GAAG,CAACE,YAAY;QAC9BC,aAAa,EAAEH,GAAG,CAACG,aAAa;QAChCrG;MACF,CAAC,CAAC;MAEFS,IAAI,CAAC+B,kBAAkB,YAAvB/B,IAAI,CAAC+B,kBAAkB,CAAG;QACxB1B,QAAQ,EAAEf,gBAAgB;QAC1B2C,cAAc,EAAEf,kBAAkB;QAClCgB,cAAc,EAAE,qBAAqB;QACrCC,eAAe,EAAE,IAAI;QACrBC,eAAe,EAAE;MACnB,CAAC,CAAC;MACF;IACF;IAEAlC,WAAW,CAAC,IAAI,CAAC;IACjBpB,MAAM,YAANA,MAAM,CAAG,CAAC;EACZ,CAAC;EAED,MAAMoH,gBAAgB,GAAGA,CAAA,KAAM;IAC7B9F,UAAU,CAAC,IAAI,CAAC;IAChB2F,OAAO,CAACI,KAAK,CAAC,uBAAuB,EAAE;MACrCC,MAAM,EAAErG,gBAAgB;MACxBM,QAAQ;MACR/B;IACF,CAAC,CAAC;IACFS,OAAO,YAAPA,OAAO,CAAG,IAAIsH,KAAK,CAAC,yBAAyBtG,gBAAgB,EAAE,CAAC,CAAC;EACnE,CAAC;EAED,MAAMuG,wBAAwB,GAAG3H,gBAAgB,KAAK,aAAa;EACnE,MAAM4H,iBAAiB,GAAGD,wBAAwB,GAC9C;IACEE,UAAU,EAAE,aAAa;IACzBC,SAAS,EAAE;EACb,CAAC,GACD;IACED,UAAU,EAAE,0BAA0B7H,gBAAgB,qBAAqBA,gBAAgB,QAAQ;IACnG+H,cAAc,EAAE,WAAW;IAC3BD,SAAS,EAAE;EACb,CAAC;EAEL,MAAME,aAAa,GAAInB,CAAsB,IAAK;IAChD,IAAIxG,OAAO,KAAKwG,CAAC,CAACoB,GAAG,KAAK,OAAO,IAAIpB,CAAC,CAACoB,GAAG,KAAK,GAAG,CAAC,EAAE;MACnDpB,CAAC,CAACqB,cAAc,CAAC,CAAC;MAClB7H,OAAO,CAAC,CAAC;IACX;EACF,CAAC;EAED,oBACE1B,KAAA,CAAAwJ,aAAA,CAAAxJ,KAAA,CAAAyJ,QAAA,qBACEzJ,KAAA,CAAAwJ,aAAA;IACEE,GAAG,EAAEjG,YAAa;IAClBxC,SAAS,EAAE,CACT,OAAO,EACPM,MAAM,KAAK,WAAW,GAAG,EAAE,GAAG,0BAA0B,EACxDsE,gBAAgB,GAAG,UAAU,GAAG,EAAE,EAClC5E,SAAS,CACV,CACEsB,MAAM,CAACoH,OAAO,CAAC,CACfC,IAAI,CAAC,GAAG,CAAE;IACbC,KAAK,EACHtI,MAAM,KAAK,WAAW,GAClBiB,SAAS,GACT;MAAEsH,SAAS,EAAE,KAAK;MAAE1D,eAAe,EAAED;IAAwB,CAClE;IACDzE,OAAO,EAAEC,SAAS,GAAGD,OAAO,GAAGc,SAAU;IACzCuH,SAAS,EAAEpI,SAAS,GAAG0H,aAAa,GAAG7G,SAAU;IACjDwH,QAAQ,EAAErI,SAAS,GAAG,CAAC,GAAGa,SAAU;IACpCyH,IAAI,EAAEtI,SAAS,GAAG,QAAQ,GAAGa,SAAU;IACvC,cAAYb,SAAS,GAAGX,GAAG,GAAGwB;EAAU,GAEvCqD,gBAAgB,iBACf7F,KAAA,CAAAwJ,aAAA;IAAMvI,SAAS,EAAC;EAA6H,GAC1I2C,kBACG,CACP,EACA,CAACjB,QAAQ,IAAI,CAACE,OAAO,IAAIJ,gBAAgB,iBACxCzC,KAAA,CAAAwJ,aAAA;IACEvI,SAAS,EAAC,gCAAgC;IAC1C4I,KAAK,EAAEZ,iBAAkB;IACzBgB,IAAI,EAAC,QAAQ;IACb,cAAW;EAAe,CAC3B,CACF,EAEApH,OAAO,iBACN7C,KAAA,CAAAwJ,aAAA;IACEvI,SAAS,EAAC,2FAA2F;IACrGgJ,IAAI,EAAC,OAAO;IACZ,aAAU;EAAW,gBAErBjK,KAAA,CAAAwJ,aAAA;IAAMvI,SAAS,EAAC;EAAuB,gBACrCjB,KAAA,CAAAwJ,aAAA;IACEvI,SAAS,EAAC,oCAAoC;IAC9CiJ,IAAI,EAAC,MAAM;IACXC,MAAM,EAAC,cAAc;IACrBC,OAAO,EAAC,WAAW;IACnB,eAAY,MAAM;IAClBC,SAAS,EAAC;EAAO,gBAEjBrK,KAAA,CAAAwJ,aAAA;IACEc,aAAa,EAAC,OAAO;IACrBC,cAAc,EAAC,OAAO;IACtBC,WAAW,EAAE,CAAE;IACfC,CAAC,EAAC;EAA2J,CAC9J,CACE,CAAC,eACNzK,KAAA,CAAAwJ,aAAA;IAAMvI,SAAS,EAAC;EAAe,GAAC,sBAA0B,CACtD,CACF,CACP,EAEA8B,QAAQ,IAAI,CAACF,OAAO,iBACnB7C,KAAA,CAAAwJ,aAAA;IACErF,GAAG,EAAEpB,QAAS;IACd/B,GAAG,EAAEA,GAAI;IACTC,SAAS,EAAE,GACTM,MAAM,KAAK,WAAW,GAClB,eAAe,GACf,gCAAgC,oCAEpCoB,QAAQ,GAAG,aAAa,GAAG,WAAW,IAEtCqD,kBAAkB,KAAK,SAAS,GAC5B,gBAAgB,GAChBA,kBAAkB,KAAK,OAAO,GAC9B,cAAc,GACdA,kBAAkB,KAAK,MAAM,GAC7B,aAAa,GACbA,kBAAkB,KAAK,MAAM,GAC7B,aAAa,GACbA,kBAAkB,KAAK,YAAY,GACnC,mBAAmB,GACnB,EAAE,EACL;IACH6D,KAAK,EACHtI,MAAM,KAAK,WAAW,GAClB;MACEiE,KAAK,EAAE,MAAM;MACbC,MAAM,EAAE,MAAM;MACdiF,QAAQ,EAAE,MAAM;MAChBC,OAAO,EAAE;IACX,CAAC,GACD;MACEC,QAAQ,EAAE,UAAU;MACpBC,KAAK,EAAE,CAAC;MACRrF,KAAK,EAAE,MAAM;MACbC,MAAM,EAAE,MAAM;MACdnE,SAAS,EAAE0E,kBAAkB;MAC7B0E,QAAQ,EAAE,MAAM;MAChBC,OAAO,EAAE;IACX,CACL;IACDnJ,MAAM,EAAEyG,eAAgB;IACxBxG,OAAO,EAAEmH,gBAAiB;IAC1BtE,OAAO,EAAEvB,QAAQ,CAAC0E,UAAU,CAAC,OAAO,CAAC,GAAG,OAAO,GAAG;EAAO,CAC1D,CAEC,CACN,CAAC;AAEP,CAAC,CAAC","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["React","useState","useRef","useEffect","memo","useMemo","getResizedImageUrl","normalizeImageUrl","loadBackdropAnalysis","isWeb5ImageUrl","useComponentDependencies","useResolvedImageSources","backdropColorOf","lowResFallbackCache","Map","OptimizedImage","props","_slot$visualMetadata","_slot$crop","alt","className","quality","format","progressive","placeholderColor","objectFit","layout","onLoad","onError","onClick","clickable","imageFromText","disableResize","imageMode","adaptiveBackdrop","slot","fallbackOnLowRes","lowResThreshold","isResolvedMode","unresolvedSource","filename","preResolvedUrl","resolvedUrl","filter","undefined","sourceIdentifier","deps","isLoaded","setIsLoaded","isError","setIsError","imageUrl","setImageUrl","isFallback","setIsFallback","backdrop","setBackdrop","isDebugImageEnabled","useDebugImageContext","dimensions","setDimensions","containerRef","observerRef","isContextualImage","contextualImageKey","rawKey","slice","length","decodeURIComponent","trim","resolutionInputs","src","images","resolvedSources","loading","isResolutionLoading","resolvedSource","logContextualImage","url","contextualTerm","imageEventType","isImageResolved","isFallbackImage","contextualImageFallback","fallbacks","fallbackImages","Math","floor","random","effectiveFilename","sourceCacheKey","hasResolvedSourceDimensions","width","height","isLowResResolvedAsset","cachedFallbackUrl","get","showDynamicBadge","adaptiveActive","adaptiveResolved","effectiveObjectFit","hasBackdrop","effectiveImageMode","adaptiveBackgroundColor","backgroundColor","slotBackground","visualMetadata","background","slotIsSolid","slotWantsContain","Boolean","crop","contained","resolvedObjectFit","resolvedBackgroundColor","token","aborted","then","result","set","container","current","updateDimensions","rect","getBoundingClientRect","ceil","window","ResizeObserver","observe","handleResize","addEventListener","disconnect","removeEventListener","normalizedUrl","isExternalNonWixUrl","startsWith","includes","imageFilename","match","replace","dpr","devicePixelRatio","optimizedUrl","handleImageLoad","e","img","currentTarget","naturalWidth","naturalHeight","originalImageUrl","currentSrc","console","warn","fallbackImageUrl","handleImageError","error","source","Error","isTransparentPlaceholder","placeholderStyles","animation","backgroundSize","handleKeyDown","key","preventDefault","createElement","Fragment","ref","join","style","minHeight","onKeyDown","tabIndex","role","fill","stroke","viewBox","focusable","strokeLinecap","strokeLinejoin","strokeWidth","d","maxWidth","display","position","inset"],"sources":["../../../../src/components/ui/OptimizedImage.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect, memo, useMemo } from 'react';\nimport { getResizedImageUrl, normalizeImageUrl } from '../../utils/image-utils';\nimport {\n loadBackdropAnalysis,\n type BackdropAnalysis,\n} from '../../utils/imageBackdrop';\nimport { isWeb5ImageUrl } from '../../types/link-types';\nimport { useComponentDependencies } from '../../context/ComponentDependenciesContext';\nimport type { ImageSearchFilterString } from '../../image/imageSearchFilterTypes';\nimport { useResolvedImageSources } from '../../hooks/useResolvedImageSources';\nimport {\n backdropColorOf,\n type ResolvedImageSlot,\n} from '../../image/imageSetTypes';\n\nconst lowResFallbackCache = new Map<string, string>();\n\ninterface OptimizedImageBaseProps {\n /** Accessible alternative text for the rendered image. */\n alt: string;\n /** Additional CSS classes applied to the outer image container. */\n className?: string;\n /** Preferred output quality for resized Wix media URLs. */\n quality?: number;\n /** Requested output format for future image delivery customization. */\n format?: 'auto' | 'webp' | 'jpg' | 'png';\n /** Enables progressive loading behavior when supported by the delivery layer. */\n progressive?: boolean;\n /** Placeholder background color shown before the image loads. */\n placeholderColor?: string;\n /** CSS object-fit behavior applied to the rendered `<img>`. */\n objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';\n /** Layout strategy for the image container. */\n layout?: 'fill' | 'intrinsic';\n /** Called after the final rendered image successfully loads. */\n onLoad?: () => void;\n /** Called when the final rendered image fails to load. */\n onError?: (error: Error) => void;\n /** Click handler for interactive image surfaces. */\n onClick?: () => void;\n /** Makes the image container keyboard-focusable and clickable. */\n clickable?: boolean;\n /** Marks the source as originating from text-driven image generation for BI logging. */\n imageFromText?: boolean;\n /** Skips Wix fitted delivery URL rewriting and uses the normalized original URL directly. */\n disableResize?: boolean;\n /** CDN resize mode. 'fill' crops to exact container dimensions (sharp at slot size).\n * 'fit' (default) preserves aspect ratio. */\n imageMode?: 'fit' | 'fill';\n /**\n * Auto-pick `objectFit`/`imageMode` per image by sniffing its edges on a\n * canvas (see `imageBackdrop.ts`). Images shot on a seamless backdrop are\n * rendered `contain` with the slot painted that backdrop colour (the subject\n * is never cropped, the letterbox is invisible); full-bleed scenes fall back\n * to `cover`. Overrides `objectFit`/`imageMode` once analysis resolves; until\n * then, and on any probe/CORS failure, the passed `objectFit`/`imageMode`\n * apply. No effect for `layout=\"intrinsic\"`. */\n adaptiveBackdrop?: boolean;\n /**\n * A slot resolved by `ResolveImageSet` (ADR 0221). Supplying it makes the\n * server's own decode authoritative over `adaptiveBackdrop`'s canvas probe:\n * fit comes from `visualMetadata.background` and the backdrop colour from\n * `backgroundColor` (falling back to the dominant swatch for a scene), so no\n * pixel is read back, no CORS is needed and nothing is guessed.\n *\n * Pair it with `resolvedUrl={slot.imageUrl}` and `disableResize` — the URL\n * already carries the crop and the render size.\n */\n slot?: ResolvedImageSlot;\n /** Full URL to a fallback image used when the primary image is considered low resolution. */\n fallbackOnLowRes?: string;\n /** Minimum acceptable width or height before low-resolution fallback is triggered. */\n lowResThreshold?: number;\n}\n\ninterface OptimizedImageWithFilenameProps {\n /** Image source to render. Supports full URLs and `web5://image/...` contextual image tokens. */\n filename: string;\n /** Optional contextual search filter used only when resolving a `web5://image/...` source. */\n filter?: ImageSearchFilterString;\n /** Mutually exclusive with `filename`; provide this only when the image URL is already resolved. */\n resolvedUrl?: never;\n}\n\ninterface OptimizedImageWithResolvedUrlProps {\n /** Already-resolved image URL. When provided, OptimizedImage skips all source resolution. */\n resolvedUrl: string;\n /** Mutually exclusive with `resolvedUrl`; use `filename` when the source still needs resolving. */\n filename?: never;\n /** Not applicable for already-resolved image URLs. */\n filter?: never;\n}\n\nexport type OptimizedImageProps = OptimizedImageBaseProps &\n (OptimizedImageWithFilenameProps | OptimizedImageWithResolvedUrlProps);\n\nexport const OptimizedImage: React.FC<OptimizedImageProps> = memo((props) => {\n const {\n alt,\n className = '',\n quality = 85,\n format = 'auto',\n progressive = true,\n placeholderColor = '#f3f4f6',\n objectFit = 'contain',\n layout = 'fill',\n onLoad,\n onError,\n onClick,\n clickable = false,\n imageFromText = false,\n disableResize = false,\n imageMode = 'fit',\n adaptiveBackdrop = false,\n slot,\n fallbackOnLowRes,\n lowResThreshold = 550,\n } = props;\n void format;\n void progressive;\n\n const isResolvedMode = 'resolvedUrl' in props;\n const unresolvedSource = 'filename' in props ? props.filename ?? '' : '';\n const preResolvedUrl = 'resolvedUrl' in props ? props.resolvedUrl : '';\n const filter = 'filename' in props ? props.filter : undefined;\n const sourceIdentifier = isResolvedMode ? preResolvedUrl : unresolvedSource;\n\n const deps = useComponentDependencies();\n const [isLoaded, setIsLoaded] = useState(false);\n const [isError, setIsError] = useState(false);\n const [imageUrl, setImageUrl] = useState<string>('');\n const [isFallback, setIsFallback] = useState(false);\n // null = adaptive analysis pending (only meaningful when adaptiveBackdrop).\n const [backdrop, setBackdrop] = useState<BackdropAnalysis | null>(null);\n const isDebugImageEnabled = deps.useDebugImageContext();\n const [dimensions, setDimensions] = useState<{\n width: number;\n height: number;\n } | null>(null);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const observerRef = useRef<ResizeObserver | null>(null);\n\n const isContextualImage = useMemo(\n () =>\n !isResolvedMode &&\n !!(unresolvedSource && isWeb5ImageUrl(unresolvedSource)),\n [isResolvedMode, unresolvedSource],\n );\n\n const contextualImageKey = useMemo(() => {\n if (!isContextualImage) {\n return '';\n }\n const rawKey = unresolvedSource.slice('web5://image/'.length);\n try {\n return decodeURIComponent(rawKey).trim();\n } catch {\n return rawKey.trim();\n }\n }, [isContextualImage, unresolvedSource]);\n const resolutionInputs = useMemo(\n () =>\n isResolvedMode || !unresolvedSource\n ? []\n : [\n {\n src: unresolvedSource,\n filter,\n },\n ],\n [filter, isResolvedMode, unresolvedSource],\n );\n const { images: resolvedSources, loading: isResolutionLoading } =\n useResolvedImageSources(resolutionInputs);\n const resolvedSource = useMemo(\n () => (!isResolvedMode ? resolvedSources?.[0] ?? null : null),\n [isResolvedMode, resolvedSources],\n );\n\n useEffect(() => {\n if (\n !isContextualImage ||\n isResolvedMode ||\n isResolutionLoading ||\n resolvedSources === null\n ) {\n return;\n }\n\n deps.logContextualImage?.({\n imageUrl: resolvedSource?.url ?? '',\n contextualTerm: contextualImageKey,\n imageEventType: imageFromText\n ? 'web5_image_link_from_text'\n : 'web5_image_link',\n isImageResolved: !!resolvedSource?.url,\n isFallbackImage: false,\n });\n }, [\n contextualImageKey,\n deps,\n imageFromText,\n isContextualImage,\n isResolvedMode,\n isResolutionLoading,\n resolvedSource?.url,\n resolvedSources,\n ]);\n\n // When imageFromText contextual image resolution returns empty, use a random fallback\n const contextualImageFallback = useMemo(() => {\n if (\n !imageFromText ||\n !isContextualImage ||\n isResolutionLoading ||\n resolvedSources === null ||\n resolvedSource?.url\n ) {\n return '';\n }\n const fallbacks = deps.fallbackImages;\n if (!fallbacks || fallbacks.length === 0) {\n return '';\n }\n return fallbacks[Math.floor(Math.random() * fallbacks.length)];\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n imageFromText,\n isContextualImage,\n isResolutionLoading,\n resolvedSources,\n resolvedSource?.url,\n deps.fallbackImages,\n ]);\n\n // Log when a fallback image is used for an unresolved contextual image\n useEffect(() => {\n if (!contextualImageFallback) {\n return;\n }\n deps.logContextualImage?.({\n imageUrl: contextualImageFallback,\n contextualTerm: contextualImageKey,\n imageEventType: 'web5_image_fallback',\n isImageResolved: false,\n isFallbackImage: true,\n });\n }, [contextualImageFallback, contextualImageKey, deps]);\n\n const effectiveFilename = isResolvedMode\n ? preResolvedUrl\n : resolvedSource?.url || contextualImageFallback;\n const sourceCacheKey = useMemo(\n () =>\n effectiveFilename ? normalizeImageUrl(effectiveFilename).trim() : '',\n [effectiveFilename],\n );\n const hasResolvedSourceDimensions =\n typeof resolvedSource?.width === 'number' &&\n typeof resolvedSource?.height === 'number';\n const isLowResResolvedAsset =\n hasResolvedSourceDimensions &&\n (resolvedSource.width! < lowResThreshold ||\n resolvedSource.height! < lowResThreshold);\n const cachedFallbackUrl = useMemo(\n () => (sourceCacheKey ? lowResFallbackCache.get(sourceCacheKey) ?? '' : ''),\n [sourceCacheKey],\n );\n const showDynamicBadge = isContextualImage && isDebugImageEnabled;\n\n // Adaptive fit: probe the resolved image's edges, then map to fit/object-fit.\n // Until analysis resolves (`backdrop === null`) the displayed URL is held back\n // so we never fetch with the wrong mode and flash a re-crop. On a backdrop we\n // render `contain` (whole subject visible) and paint the slot its colour; on a\n // busy scene we `cover`. Failures resolve to `hasBackdrop: false` → `cover`.\n const adaptiveActive = adaptiveBackdrop && layout !== 'intrinsic';\n const adaptiveResolved = !adaptiveActive || backdrop !== null;\n const effectiveObjectFit = adaptiveActive\n ? backdrop?.hasBackdrop\n ? 'contain'\n : 'cover'\n : objectFit;\n const effectiveImageMode = adaptiveActive\n ? backdrop?.hasBackdrop\n ? 'fit'\n : 'fill'\n : imageMode;\n const adaptiveBackgroundColor =\n adaptiveActive && backdrop?.hasBackdrop\n ? backdrop.backgroundColor\n : undefined;\n\n // A resolved slot answers the same two questions the canvas probe was asking,\n // from the server's own decode. It wins wherever it is present.\n //\n // `contained` (ADR 0220) forces `contain` regardless of anything else: the\n // server deliberately returned a crop at a different aspect than the slot\n // asked for, so that the whole product stays in frame. Cropping it again with\n // `cover` would undo exactly the cut it went out of its way to avoid, and the\n // padding it leaves behind is what `backgroundColor` is for — guaranteed\n // non-empty in that case.\n const slotBackground = slot?.visualMetadata?.background;\n const slotIsSolid = slotBackground === 'IMAGE_BACKGROUND_SOLID';\n const slotWantsContain = Boolean(slot?.crop?.contained) || slotIsSolid;\n const resolvedObjectFit = slot\n ? slotWantsContain\n ? 'contain'\n : 'cover'\n : effectiveObjectFit;\n const resolvedBackgroundColor = slot\n ? slotWantsContain\n ? backdropColorOf(slot)\n : undefined\n : adaptiveBackgroundColor;\n\n useEffect(() => {\n if (!adaptiveActive) {\n return;\n }\n setBackdrop(null);\n if (\n !effectiveFilename ||\n isFallback ||\n cachedFallbackUrl ||\n (isLowResResolvedAsset && fallbackOnLowRes)\n ) {\n // No analyzable primary source → behave like a busy scene (cover).\n setBackdrop({ hasBackdrop: false });\n return;\n }\n const token = { aborted: false };\n loadBackdropAnalysis(effectiveFilename, token).then((result) => {\n if (!token.aborted) {\n setBackdrop(result);\n }\n });\n return () => {\n token.aborted = true;\n };\n }, [\n adaptiveActive,\n effectiveFilename,\n isFallback,\n cachedFallbackUrl,\n isLowResResolvedAsset,\n fallbackOnLowRes,\n ]);\n\n useEffect(() => {\n setIsLoaded(false);\n setIsError(false);\n setIsFallback(false);\n setImageUrl('');\n }, [sourceCacheKey, fallbackOnLowRes]);\n\n useEffect(() => {\n if (!sourceCacheKey || !fallbackOnLowRes) {\n return;\n }\n\n if (isLowResResolvedAsset) {\n lowResFallbackCache.set(sourceCacheKey, fallbackOnLowRes);\n setIsFallback(true);\n setImageUrl(fallbackOnLowRes);\n return;\n }\n\n if (!cachedFallbackUrl) {\n return;\n }\n\n setIsFallback(true);\n setImageUrl(cachedFallbackUrl);\n }, [\n cachedFallbackUrl,\n fallbackOnLowRes,\n isLowResResolvedAsset,\n sourceCacheKey,\n ]);\n\n // For fill layout, measure container so we can request a resized Wix image.\n useEffect(() => {\n if (layout === 'intrinsic' || disableResize) {\n return;\n }\n const container = containerRef.current;\n if (!container) {\n return;\n }\n\n const updateDimensions = () => {\n const rect = container.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n setDimensions({\n width: Math.ceil(rect.width),\n height: Math.ceil(rect.height),\n });\n }\n };\n\n updateDimensions();\n\n if ('ResizeObserver' in window) {\n observerRef.current = new ResizeObserver(() => {\n updateDimensions();\n });\n observerRef.current.observe(container);\n }\n\n const handleResize = () => updateDimensions();\n window.addEventListener('resize', handleResize);\n\n return () => {\n if (observerRef.current) {\n observerRef.current.disconnect();\n }\n window.removeEventListener('resize', handleResize);\n };\n }, [disableResize, layout]);\n\n // Intrinsic layout: use normalized URL without resize logic.\n useEffect(() => {\n if (layout !== 'intrinsic') {\n return;\n }\n if (\n !effectiveFilename ||\n isFallback ||\n cachedFallbackUrl ||\n (isLowResResolvedAsset && fallbackOnLowRes)\n ) {\n return;\n }\n\n const normalizedUrl = normalizeImageUrl(effectiveFilename);\n setImageUrl(normalizedUrl);\n }, [\n cachedFallbackUrl,\n effectiveFilename,\n isFallback,\n isLowResResolvedAsset,\n layout,\n ]);\n\n // External non-Wix URLs skip resizing; use them immediately.\n useEffect(() => {\n if (layout === 'intrinsic') {\n return;\n }\n if (\n !effectiveFilename ||\n isFallback ||\n cachedFallbackUrl ||\n (isLowResResolvedAsset && fallbackOnLowRes)\n ) {\n return;\n }\n\n const normalizedUrl = normalizeImageUrl(effectiveFilename);\n const isExternalNonWixUrl =\n (normalizedUrl.startsWith('http://') ||\n normalizedUrl.startsWith('https://')) &&\n !normalizedUrl.includes('static.wixstatic.com');\n\n if (isExternalNonWixUrl) {\n setImageUrl(normalizedUrl);\n }\n }, [\n cachedFallbackUrl,\n effectiveFilename,\n isFallback,\n isLowResResolvedAsset,\n layout,\n ]);\n\n // Explicit opt-out: use original normalized URL even in fill layouts.\n useEffect(() => {\n if (layout === 'intrinsic') {\n return;\n }\n if (\n !disableResize ||\n !effectiveFilename ||\n isFallback ||\n cachedFallbackUrl ||\n (isLowResResolvedAsset && fallbackOnLowRes)\n ) {\n return;\n }\n\n setImageUrl(normalizeImageUrl(effectiveFilename));\n }, [\n cachedFallbackUrl,\n disableResize,\n effectiveFilename,\n isFallback,\n isLowResResolvedAsset,\n layout,\n ]);\n\n // Wix/local URLs: wait for dimensions, then build a resized URL.\n useEffect(() => {\n if (layout === 'intrinsic') {\n return;\n }\n if (disableResize) {\n return;\n }\n // Hold the fetch until adaptive analysis picks fit vs fill, so we request\n // the right CDN mode once instead of fetching twice and re-cropping.\n if (!adaptiveResolved) {\n return;\n }\n if (\n !dimensions ||\n !effectiveFilename ||\n isFallback ||\n cachedFallbackUrl ||\n (isLowResResolvedAsset && fallbackOnLowRes)\n ) {\n return;\n }\n\n const normalizedUrl = normalizeImageUrl(effectiveFilename);\n const isExternalNonWixUrl =\n (normalizedUrl.startsWith('http://') ||\n normalizedUrl.startsWith('https://')) &&\n !normalizedUrl.includes('static.wixstatic.com');\n\n if (isExternalNonWixUrl) {\n return;\n }\n\n let imageFilename = effectiveFilename;\n if (normalizedUrl.includes('static.wixstatic.com')) {\n const match = normalizedUrl.match(/media\\/([^/?]+)/);\n if (match && match[1]) {\n imageFilename = match[1];\n } else {\n setImageUrl(normalizedUrl);\n return;\n }\n } else if (\n normalizedUrl.startsWith('https://static.wixstatic.com/media/')\n ) {\n imageFilename = normalizedUrl.replace(\n 'https://static.wixstatic.com/media/',\n '',\n );\n }\n\n const dpr = window.devicePixelRatio ?? 1;\n const optimizedUrl = getResizedImageUrl(\n imageFilename,\n dimensions.width * dpr,\n dimensions.height * dpr,\n { quality, imageMode: effectiveImageMode },\n );\n setImageUrl(optimizedUrl);\n }, [\n dimensions,\n cachedFallbackUrl,\n effectiveFilename,\n isFallback,\n isLowResResolvedAsset,\n layout,\n disableResize,\n quality,\n effectiveImageMode,\n adaptiveResolved,\n ]);\n\n const handleImageLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {\n const img = e.currentTarget;\n\n if (\n fallbackOnLowRes &&\n !isFallback &&\n !hasResolvedSourceDimensions &&\n (img.naturalWidth < lowResThreshold ||\n img.naturalHeight < lowResThreshold)\n ) {\n const originalImageUrl =\n img.currentSrc || imageUrl || effectiveFilename || sourceIdentifier;\n\n if (sourceCacheKey) {\n lowResFallbackCache.set(sourceCacheKey, fallbackOnLowRes);\n }\n\n setIsFallback(true);\n setImageUrl(fallbackOnLowRes);\n setIsLoaded(false);\n\n console.warn('OptimizedImage applied a low-res fallback image.', {\n originalImageUrl,\n fallbackImageUrl: fallbackOnLowRes,\n naturalWidth: img.naturalWidth,\n naturalHeight: img.naturalHeight,\n lowResThreshold,\n });\n\n deps.logContextualImage?.({\n imageUrl: fallbackOnLowRes,\n contextualTerm: contextualImageKey,\n imageEventType: 'web5_image_fallback',\n isImageResolved: true,\n isFallbackImage: true,\n });\n return;\n }\n\n setIsLoaded(true);\n onLoad?.();\n };\n\n const handleImageError = () => {\n setIsError(true);\n console.error('Failed to load image:', {\n source: sourceIdentifier,\n imageUrl,\n alt,\n });\n onError?.(new Error(`Failed to load image: ${sourceIdentifier}`));\n };\n\n const isTransparentPlaceholder = placeholderColor === 'transparent';\n const placeholderStyles = isTransparentPlaceholder\n ? {\n background: 'transparent',\n animation: 'none',\n }\n : {\n background: `linear-gradient(90deg, ${placeholderColor} 0%, #e5e7eb 50%, ${placeholderColor} 100%)`,\n backgroundSize: '200% 100%',\n animation: 'shimmer 2s infinite ease-in-out',\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (onClick && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onClick();\n }\n };\n\n return (\n <>\n <span\n ref={containerRef}\n className={[\n 'block',\n layout === 'intrinsic' ? '' : 'relative overflow-hidden',\n showDynamicBadge ? 'relative' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={\n layout === 'intrinsic'\n ? undefined\n : { minHeight: '1px', backgroundColor: resolvedBackgroundColor }\n }\n onClick={clickable ? onClick : undefined}\n onKeyDown={clickable ? handleKeyDown : undefined}\n tabIndex={clickable ? 0 : undefined}\n role={clickable ? 'button' : undefined}\n aria-label={clickable ? alt : undefined}\n >\n {showDynamicBadge && (\n <span className=\"absolute left-2 top-2 z-10 rounded bg-blue-600/90 px-2 py-1 text-[10px] font-semibold tracking-wide text-white shadow block\">\n {contextualImageKey}\n </span>\n )}\n {!isLoaded && !isError && sourceIdentifier && (\n <span\n className=\"absolute inset-0 rounded block\"\n style={placeholderStyles}\n role=\"status\"\n aria-label=\"Loading image\"\n />\n )}\n\n {isError && (\n <span\n className=\"absolute inset-0 flex items-center justify-center bg-gray-100 text-gray-500 rounded block\"\n role=\"alert\"\n aria-live=\"assertive\"\n >\n <span className=\"text-center p-4 block\">\n <svg\n className=\"w-8 h-8 mx-auto mb-2 text-gray-400\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n <span className=\"text-xs block\">Failed to load image</span>\n </span>\n </span>\n )}\n\n {imageUrl && !isError && (\n <img\n src={imageUrl}\n alt={alt}\n className={`${\n layout === 'intrinsic'\n ? 'w-full h-auto'\n : 'absolute inset-0 w-full h-full'\n } transition-opacity duration-300 ${\n isLoaded ? 'opacity-100' : 'opacity-0'\n } ${\n resolvedObjectFit === 'contain'\n ? 'object-contain'\n : resolvedObjectFit === 'cover'\n ? 'object-cover'\n : resolvedObjectFit === 'fill'\n ? 'object-fill'\n : resolvedObjectFit === 'none'\n ? 'object-none'\n : resolvedObjectFit === 'scale-down'\n ? 'object-scale-down'\n : ''\n }`}\n style={\n layout === 'intrinsic'\n ? {\n width: '100%',\n height: 'auto',\n maxWidth: '100%',\n display: 'block',\n }\n : {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n objectFit: resolvedObjectFit,\n maxWidth: '100%',\n display: 'block',\n }\n }\n onLoad={handleImageLoad}\n onError={handleImageError}\n loading={imageUrl.startsWith('data:') ? 'eager' : 'lazy'}\n />\n )}\n </span>\n </>\n );\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { MAX_IMAGE_SLOTS_PER_SET } from '../image/imageSetTypes.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The page-level image collector (ADR 0221).
|
|
6
|
+
*
|
|
7
|
+
* Sections declare slots; this provider resolves them together. That is the
|
|
8
|
+
* whole point: a per-component call to a set-level endpoint is a one-slot
|
|
9
|
+
* request, which throws away the page-wide assignment, the per-kind mode
|
|
10
|
+
* negotiation and the cohesion that are the only reasons the endpoint takes a
|
|
11
|
+
* list at all.
|
|
12
|
+
*
|
|
13
|
+
* The shape is deliberately one-shot per turn. Slots register during the
|
|
14
|
+
* commit that renders them, the provider flushes once on the next microtask,
|
|
15
|
+
* and a slot that registers after that flush does NOT trigger a second
|
|
16
|
+
* request — it reports `late` and the caller falls back. A page that asks
|
|
17
|
+
* twice cannot be coherent, so asking twice is not offered.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const IDLE_STATE = {
|
|
21
|
+
status: 'unavailable'
|
|
22
|
+
};
|
|
23
|
+
const ImageSetContext = /*#__PURE__*/createContext(null);
|
|
24
|
+
export const ImageSetProvider = _ref => {
|
|
25
|
+
let {
|
|
26
|
+
resolveImageSet,
|
|
27
|
+
preferredMode,
|
|
28
|
+
turnId,
|
|
29
|
+
maxSlots = MAX_IMAGE_SLOTS_PER_SET,
|
|
30
|
+
children
|
|
31
|
+
} = _ref;
|
|
32
|
+
// Registration is refs, not state: a slot registering must not re-render the
|
|
33
|
+
// whole tree mid-collection. Subscribers are notified explicitly once the
|
|
34
|
+
// answer lands.
|
|
35
|
+
const pending = useRef(new Map());
|
|
36
|
+
const states = useRef(new Map());
|
|
37
|
+
const listeners = useRef(new Set());
|
|
38
|
+
const flushed = useRef(false);
|
|
39
|
+
const scheduled = useRef(false);
|
|
40
|
+
const notify = useCallback(() => {
|
|
41
|
+
listeners.current.forEach(l => l());
|
|
42
|
+
}, []);
|
|
43
|
+
|
|
44
|
+
// A new turn retires the previous set wholesale — and this MUST happen in the
|
|
45
|
+
// render phase, not an effect. Child effects run before the parent's, so a
|
|
46
|
+
// reset in `useEffect` would fire *after* this turn's slots had registered
|
|
47
|
+
// and would wipe them, leaving the flush with nothing to send. The parent
|
|
48
|
+
// renders first, so resetting here lands before any child registers.
|
|
49
|
+
const lastTurn = useRef(turnId);
|
|
50
|
+
const started = useRef(false);
|
|
51
|
+
if (!started.current) {
|
|
52
|
+
started.current = true;
|
|
53
|
+
} else if (lastTurn.current !== turnId) {
|
|
54
|
+
lastTurn.current = turnId;
|
|
55
|
+
pending.current.clear();
|
|
56
|
+
states.current.clear();
|
|
57
|
+
flushed.current = false;
|
|
58
|
+
scheduled.current = false;
|
|
59
|
+
}
|
|
60
|
+
const flush = useCallback(async () => {
|
|
61
|
+
scheduled.current = false;
|
|
62
|
+
if (flushed.current || !resolveImageSet) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const images = Array.from(pending.current.values());
|
|
66
|
+
if (images.length === 0) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Everything registered before the flush goes in one request; the set is
|
|
70
|
+
// closed the moment it is sent.
|
|
71
|
+
flushed.current = true;
|
|
72
|
+
try {
|
|
73
|
+
const result = await resolveImageSet({
|
|
74
|
+
images,
|
|
75
|
+
preferredMode
|
|
76
|
+
});
|
|
77
|
+
const bySlotId = new Map();
|
|
78
|
+
// The contract guarantees same length and order, but keying by the echoed
|
|
79
|
+
// slotId means a server that ever breaks that does not silently
|
|
80
|
+
// mis-attribute someone else's photograph to this slot.
|
|
81
|
+
(result.slots ?? []).forEach((s, i) => {
|
|
82
|
+
var _images$i;
|
|
83
|
+
const id = s.slotId || ((_images$i = images[i]) == null ? void 0 : _images$i.id);
|
|
84
|
+
if (id) {
|
|
85
|
+
bySlotId.set(id, s);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
images.forEach(req => {
|
|
89
|
+
const slot = bySlotId.get(req.id);
|
|
90
|
+
states.current.set(req.id, {
|
|
91
|
+
status: slot ? 'resolved' : 'failed',
|
|
92
|
+
slot,
|
|
93
|
+
mode: result.mode
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
} catch {
|
|
97
|
+
// A failed set is not a broken page: every slot falls back to whatever it
|
|
98
|
+
// rendered before.
|
|
99
|
+
images.forEach(req => {
|
|
100
|
+
states.current.set(req.id, {
|
|
101
|
+
status: 'failed'
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
notify();
|
|
106
|
+
}, [resolveImageSet, preferredMode, notify]);
|
|
107
|
+
const register = useCallback(slot => {
|
|
108
|
+
var _states$current$get2;
|
|
109
|
+
if (!resolveImageSet) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (flushed.current) {
|
|
113
|
+
var _states$current$get;
|
|
114
|
+
// The set has already gone. Say so rather than open a second one.
|
|
115
|
+
if (((_states$current$get = states.current.get(slot.id)) == null ? void 0 : _states$current$get.status) !== 'resolved') {
|
|
116
|
+
states.current.set(slot.id, {
|
|
117
|
+
status: 'late'
|
|
118
|
+
});
|
|
119
|
+
notify();
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (!pending.current.has(slot.id) && pending.current.size >= maxSlots) {
|
|
124
|
+
states.current.set(slot.id, {
|
|
125
|
+
status: 'late'
|
|
126
|
+
});
|
|
127
|
+
notify();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
pending.current.set(slot.id, slot);
|
|
131
|
+
if (((_states$current$get2 = states.current.get(slot.id)) == null ? void 0 : _states$current$get2.status) !== 'resolved') {
|
|
132
|
+
states.current.set(slot.id, {
|
|
133
|
+
status: 'pending'
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (!scheduled.current) {
|
|
137
|
+
scheduled.current = true;
|
|
138
|
+
// One microtask after the commit that declared the slots: late enough
|
|
139
|
+
// that every section on the page has registered, early enough that the
|
|
140
|
+
// request is in flight before paint.
|
|
141
|
+
queueMicrotask(() => {
|
|
142
|
+
void flush();
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}, [resolveImageSet, maxSlots, flush, notify]);
|
|
146
|
+
const unregister = useCallback(id => {
|
|
147
|
+
// Only meaningful before the flush; afterwards the set is immutable.
|
|
148
|
+
if (!flushed.current) {
|
|
149
|
+
pending.current.delete(id);
|
|
150
|
+
states.current.delete(id);
|
|
151
|
+
}
|
|
152
|
+
}, []);
|
|
153
|
+
const stateOf = useCallback(id => states.current.get(id) ?? (resolveImageSet ? {
|
|
154
|
+
status: 'pending'
|
|
155
|
+
} : IDLE_STATE), [resolveImageSet]);
|
|
156
|
+
const subscribe = useCallback(listener => {
|
|
157
|
+
listeners.current.add(listener);
|
|
158
|
+
return () => {
|
|
159
|
+
listeners.current.delete(listener);
|
|
160
|
+
};
|
|
161
|
+
}, []);
|
|
162
|
+
const value = useMemo(() => ({
|
|
163
|
+
register,
|
|
164
|
+
unregister,
|
|
165
|
+
stateOf,
|
|
166
|
+
subscribe,
|
|
167
|
+
enabled: Boolean(resolveImageSet)
|
|
168
|
+
}), [register, unregister, stateOf, subscribe, resolveImageSet]);
|
|
169
|
+
return /*#__PURE__*/React.createElement(ImageSetContext.Provider, {
|
|
170
|
+
value: value
|
|
171
|
+
}, children);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Declare one image slot and read back what the page-wide resolution gave it.
|
|
176
|
+
*
|
|
177
|
+
* Returns `{ status: 'unavailable' }` when no provider or no resolver is
|
|
178
|
+
* present — storybook, tests, a host that has not adopted this yet — so a
|
|
179
|
+
* caller can render its existing image path unchanged in that case rather than
|
|
180
|
+
* guard on the provider's existence.
|
|
181
|
+
*
|
|
182
|
+
* `request` is read on every render but only its identity matters for
|
|
183
|
+
* re-registration, so callers should memoise it (or let its fields be stable).
|
|
184
|
+
*/
|
|
185
|
+
export function useImageSlot(request) {
|
|
186
|
+
const ctx = useContext(ImageSetContext);
|
|
187
|
+
const [, forceRender] = useState(0);
|
|
188
|
+
const id = request == null ? void 0 : request.id;
|
|
189
|
+
|
|
190
|
+
// Subscribe BEFORE registering, and keep that order: registering can settle
|
|
191
|
+
// this slot synchronously — a slot arriving after the set was already sent is
|
|
192
|
+
// told `late` inside `register` itself — and a notification fired before this
|
|
193
|
+
// component was listening would be lost, leaving it stuck on `pending`
|
|
194
|
+
// forever. Effects run in declaration order, so this one wins.
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (!ctx) {
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
return ctx.subscribe(() => forceRender(n => n + 1));
|
|
200
|
+
}, [ctx]);
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
if (!ctx || !request) {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
ctx.register(request);
|
|
206
|
+
return () => ctx.unregister(request.id);
|
|
207
|
+
// Registration is keyed on the slot's own content: a section that changes
|
|
208
|
+
// what it is asking for re-declares, one that merely re-renders does not.
|
|
209
|
+
}, [ctx, request == null ? void 0 : request.id, request == null ? void 0 : request.kind, request == null ? void 0 : request.ratio, request == null ? void 0 : request.semantic, request == null ? void 0 : request.entityId, request == null ? void 0 : request.renderWidthPx]);
|
|
210
|
+
if (!ctx || !id) {
|
|
211
|
+
return IDLE_STATE;
|
|
212
|
+
}
|
|
213
|
+
return ctx.stateOf(id);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** True when a host has wired a resolver — i.e. slots will actually resolve. */
|
|
217
|
+
export function useImageSetEnabled() {
|
|
218
|
+
var _useContext;
|
|
219
|
+
return ((_useContext = useContext(ImageSetContext)) == null ? void 0 : _useContext.enabled) ?? false;
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=ImageSetContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["React","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","MAX_IMAGE_SLOTS_PER_SET","IDLE_STATE","status","ImageSetContext","ImageSetProvider","_ref","resolveImageSet","preferredMode","turnId","maxSlots","children","pending","Map","states","listeners","Set","flushed","scheduled","notify","current","forEach","l","lastTurn","started","clear","flush","images","Array","from","values","length","result","bySlotId","slots","s","i","_images$i","id","slotId","set","req","slot","get","mode","register","_states$current$get2","_states$current$get","has","size","queueMicrotask","unregister","delete","stateOf","subscribe","listener","add","value","enabled","Boolean","createElement","Provider","useImageSlot","request","ctx","forceRender","undefined","n","kind","ratio","semantic","entityId","renderWidthPx","useImageSetEnabled","_useContext"],"sources":["../../../src/context/ImageSetContext.tsx"],"sourcesContent":["import React, {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type FC,\n type PropsWithChildren,\n} from 'react';\nimport {\n MAX_IMAGE_SLOTS_PER_SET,\n type ImageBackground,\n type ImageSlot,\n type ResolveImageSetResult,\n type ResolvedImageSlot,\n} from '../image/imageSetTypes';\n\n/**\n * The page-level image collector (ADR 0221).\n *\n * Sections declare slots; this provider resolves them together. That is the\n * whole point: a per-component call to a set-level endpoint is a one-slot\n * request, which throws away the page-wide assignment, the per-kind mode\n * negotiation and the cohesion that are the only reasons the endpoint takes a\n * list at all.\n *\n * The shape is deliberately one-shot per turn. Slots register during the\n * commit that renders them, the provider flushes once on the next microtask,\n * and a slot that registers after that flush does NOT trigger a second\n * request — it reports `late` and the caller falls back. A page that asks\n * twice cannot be coherent, so asking twice is not offered.\n */\n\ntype SlotStatus =\n /** No resolver available — the host did not supply one. */\n | 'unavailable'\n /** Registered, waiting for the flush or the response. */\n | 'pending'\n /** The response arrived and carried this slot. */\n | 'resolved'\n /** Registered after the set had already been sent; take the old path. */\n | 'late'\n /** The request failed. */\n | 'failed';\n\nexport interface ImageSlotState {\n status: SlotStatus;\n slot?: ResolvedImageSlot;\n /** The mode the whole set settled on, once known. */\n mode?: ImageBackground;\n}\n\ninterface ImageSetContextValue {\n register: (slot: ImageSlot) => void;\n unregister: (id: string) => void;\n stateOf: (id: string) => ImageSlotState;\n subscribe: (listener: () => void) => () => void;\n enabled: boolean;\n}\n\nconst IDLE_STATE: ImageSlotState = { status: 'unavailable' };\n\nconst ImageSetContext = createContext<ImageSetContextValue | null>(null);\n\nexport interface ImageSetProviderProps extends PropsWithChildren {\n /**\n * Resolves one set. Supplied by the host rather than called directly, so core\n * carries no transport: see `ComponentDependencies.resolveImageSet`.\n */\n resolveImageSet?: (input: {\n images: ImageSlot[];\n preferredMode?: ImageBackground;\n }) => Promise<ResolveImageSetResult>;\n /** Passed through as a tie-break term; never a gate (ADR 0197). */\n preferredMode?: ImageBackground;\n /**\n * Identifies the turn. Changing it starts a new set — the previous turn's\n * results are dropped and slots re-register. Without it the provider resolves\n * exactly once for its lifetime.\n */\n turnId?: string;\n /**\n * Slots beyond this many are refused with `late` rather than silently\n * dropped by the server. Defaults to the contract's own cap of 24.\n */\n maxSlots?: number;\n}\n\nexport const ImageSetProvider: FC<ImageSetProviderProps> = ({\n resolveImageSet,\n preferredMode,\n turnId,\n maxSlots = MAX_IMAGE_SLOTS_PER_SET,\n children,\n}) => {\n // Registration is refs, not state: a slot registering must not re-render the\n // whole tree mid-collection. Subscribers are notified explicitly once the\n // answer lands.\n const pending = useRef(new Map<string, ImageSlot>());\n const states = useRef(new Map<string, ImageSlotState>());\n const listeners = useRef(new Set<() => void>());\n const flushed = useRef(false);\n const scheduled = useRef(false);\n\n const notify = useCallback(() => {\n listeners.current.forEach((l) => l());\n }, []);\n\n // A new turn retires the previous set wholesale — and this MUST happen in the\n // render phase, not an effect. Child effects run before the parent's, so a\n // reset in `useEffect` would fire *after* this turn's slots had registered\n // and would wipe them, leaving the flush with nothing to send. The parent\n // renders first, so resetting here lands before any child registers.\n const lastTurn = useRef<string | undefined>(turnId);\n const started = useRef(false);\n if (!started.current) {\n started.current = true;\n } else if (lastTurn.current !== turnId) {\n lastTurn.current = turnId;\n pending.current.clear();\n states.current.clear();\n flushed.current = false;\n scheduled.current = false;\n }\n\n const flush = useCallback(async () => {\n scheduled.current = false;\n if (flushed.current || !resolveImageSet) {\n return;\n }\n const images = Array.from(pending.current.values());\n if (images.length === 0) {\n return;\n }\n // Everything registered before the flush goes in one request; the set is\n // closed the moment it is sent.\n flushed.current = true;\n\n try {\n const result = await resolveImageSet({ images, preferredMode });\n const bySlotId = new Map<string, ResolvedImageSlot>();\n // The contract guarantees same length and order, but keying by the echoed\n // slotId means a server that ever breaks that does not silently\n // mis-attribute someone else's photograph to this slot.\n (result.slots ?? []).forEach((s, i) => {\n const id = s.slotId || images[i]?.id;\n if (id) {\n bySlotId.set(id, s);\n }\n });\n images.forEach((req) => {\n const slot = bySlotId.get(req.id);\n states.current.set(req.id, {\n status: slot ? 'resolved' : 'failed',\n slot,\n mode: result.mode,\n });\n });\n } catch {\n // A failed set is not a broken page: every slot falls back to whatever it\n // rendered before.\n images.forEach((req) => {\n states.current.set(req.id, { status: 'failed' });\n });\n }\n notify();\n }, [resolveImageSet, preferredMode, notify]);\n\n const register = useCallback(\n (slot: ImageSlot) => {\n if (!resolveImageSet) {\n return;\n }\n if (flushed.current) {\n // The set has already gone. Say so rather than open a second one.\n if (states.current.get(slot.id)?.status !== 'resolved') {\n states.current.set(slot.id, { status: 'late' });\n notify();\n }\n return;\n }\n if (!pending.current.has(slot.id) && pending.current.size >= maxSlots) {\n states.current.set(slot.id, { status: 'late' });\n notify();\n return;\n }\n pending.current.set(slot.id, slot);\n if (states.current.get(slot.id)?.status !== 'resolved') {\n states.current.set(slot.id, { status: 'pending' });\n }\n if (!scheduled.current) {\n scheduled.current = true;\n // One microtask after the commit that declared the slots: late enough\n // that every section on the page has registered, early enough that the\n // request is in flight before paint.\n queueMicrotask(() => {\n void flush();\n });\n }\n },\n [resolveImageSet, maxSlots, flush, notify],\n );\n\n const unregister = useCallback((id: string) => {\n // Only meaningful before the flush; afterwards the set is immutable.\n if (!flushed.current) {\n pending.current.delete(id);\n states.current.delete(id);\n }\n }, []);\n\n const stateOf = useCallback(\n (id: string): ImageSlotState =>\n states.current.get(id) ??\n (resolveImageSet ? { status: 'pending' } : IDLE_STATE),\n [resolveImageSet],\n );\n\n const subscribe = useCallback((listener: () => void) => {\n listeners.current.add(listener);\n return () => {\n listeners.current.delete(listener);\n };\n }, []);\n\n const value = useMemo<ImageSetContextValue>(\n () => ({\n register,\n unregister,\n stateOf,\n subscribe,\n enabled: Boolean(resolveImageSet),\n }),\n [register, unregister, stateOf, subscribe, resolveImageSet],\n );\n\n return (\n <ImageSetContext.Provider value={value}>\n {children}\n </ImageSetContext.Provider>\n );\n};\n\n/**\n * Declare one image slot and read back what the page-wide resolution gave it.\n *\n * Returns `{ status: 'unavailable' }` when no provider or no resolver is\n * present — storybook, tests, a host that has not adopted this yet — so a\n * caller can render its existing image path unchanged in that case rather than\n * guard on the provider's existence.\n *\n * `request` is read on every render but only its identity matters for\n * re-registration, so callers should memoise it (or let its fields be stable).\n */\nexport function useImageSlot(request: ImageSlot | undefined): ImageSlotState {\n const ctx = useContext(ImageSetContext);\n const [, forceRender] = useState(0);\n\n const id = request?.id;\n\n // Subscribe BEFORE registering, and keep that order: registering can settle\n // this slot synchronously — a slot arriving after the set was already sent is\n // told `late` inside `register` itself — and a notification fired before this\n // component was listening would be lost, leaving it stuck on `pending`\n // forever. Effects run in declaration order, so this one wins.\n useEffect(() => {\n if (!ctx) {\n return undefined;\n }\n return ctx.subscribe(() => forceRender((n) => n + 1));\n }, [ctx]);\n\n useEffect(() => {\n if (!ctx || !request) {\n return undefined;\n }\n ctx.register(request);\n return () => ctx.unregister(request.id);\n // Registration is keyed on the slot's own content: a section that changes\n // what it is asking for re-declares, one that merely re-renders does not.\n }, [\n ctx,\n request?.id,\n request?.kind,\n request?.ratio,\n request?.semantic,\n request?.entityId,\n request?.renderWidthPx,\n ]);\n\n if (!ctx || !id) {\n return IDLE_STATE;\n }\n return ctx.stateOf(id);\n}\n\n/** True when a host has wired a resolver — i.e. slots will actually resolve. */\nexport function useImageSetEnabled(): boolean {\n return useContext(ImageSetContext)?.enabled ?? false;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `ResolveImageSet` contract, as the client sees it.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors `wix.enterprise.web_five.v1.ImageService/ResolveImageSet` (ADR 0143,
|
|
5
|
+
* 0148, 0187, 0197, 0220). Declared here rather than imported from the
|
|
6
|
+
* ambassador package so core carries no transport dependency: the host supplies
|
|
7
|
+
* `ComponentDependencies.resolveImageSet` and owns the wire mapping, including
|
|
8
|
+
* the fact that responses come back `preserving_proto_field_name`.
|
|
9
|
+
*
|
|
10
|
+
* String unions rather than enums so the values are exactly the wire's and no
|
|
11
|
+
* runtime object ships with them.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** How an image sits on its background. */
|
|
15
|
+
|
|
16
|
+
/** What a slot is for, which decides what it may name and how it is scored. */
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* How honestly a slot's image answers what the slot asked for.
|
|
20
|
+
*
|
|
21
|
+
* Measured, and load-bearing: this is the ONLY field that separates a hit from
|
|
22
|
+
* a miss. `score` cannot do it — a FALLBACK has been observed at 0.995 against
|
|
23
|
+
* a correct EXACT at 0.86 in the same response — and neither field may be
|
|
24
|
+
* compared across responses, because both are properties of the page-wide
|
|
25
|
+
* assignment rather than of the slot. The same entity slot resolving to the
|
|
26
|
+
* same image with a byte-identical crop has come back DEGRADED in one request
|
|
27
|
+
* and EXACT in another, differing only in what else shared the page.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/** A crop in source pixels of the image the slot was given. */
|
|
31
|
+
|
|
32
|
+
/** A box in normalised [0,1] coordinates — a fraction of the image's own size. */
|
|
33
|
+
|
|
34
|
+
/** A square grid of per-cell values, `edge` on a side. */
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The feature record the resolver already decoded for its own scoring, returned
|
|
38
|
+
* rather than discarded (ADR 0187).
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/** One slot a section declares. */
|
|
42
|
+
|
|
43
|
+
/** The set's own cap — 24 slots per request, per the proto's `maxSize`. */
|
|
44
|
+
export const MAX_IMAGE_SLOTS_PER_SET = 24;
|
|
45
|
+
|
|
46
|
+
/** What the server substitutes for an absent or zero `renderWidthPx`. */
|
|
47
|
+
export const DEFAULT_RENDER_WIDTH_PX = 800;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Above this, `MediaTransformUrl` refuses to compose the crop and returns the
|
|
51
|
+
* uncropped original. Callers should clamp rather than let a device-pixel-ratio
|
|
52
|
+
* multiplication carry them past it.
|
|
53
|
+
*/
|
|
54
|
+
export const MAX_RENDER_WIDTH_PX = 5000;
|
|
55
|
+
|
|
56
|
+
/** True when the slot carries a usable image (as opposed to a hole). */
|
|
57
|
+
export function hasSlotImage(slot) {
|
|
58
|
+
return Boolean(slot == null ? void 0 : slot.imageUrl);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* True when the slot's image actually depicts what the slot asked for.
|
|
63
|
+
*
|
|
64
|
+
* FALLBACK means the resolver had nothing for this subject and returned
|
|
65
|
+
* something rather than a hole — legitimate for a decorative slot, wrong for
|
|
66
|
+
* one captioned as a particular product.
|
|
67
|
+
*/
|
|
68
|
+
export function isSubjectMatch(slot) {
|
|
69
|
+
return (slot == null ? void 0 : slot.match) === 'IMAGE_MATCH_QUALITY_EXACT' || (slot == null ? void 0 : slot.match) === 'IMAGE_MATCH_QUALITY_DEGRADED';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The colour to paint behind a `contain`-fitted image so its own backdrop does
|
|
74
|
+
* not read as a rectangle against the surface. `backgroundColor` when the image
|
|
75
|
+
* is SOLID, the dominant palette swatch otherwise, and `undefined` when neither
|
|
76
|
+
* is known.
|
|
77
|
+
*/
|
|
78
|
+
export function backdropColorOf(slot) {
|
|
79
|
+
var _meta$palette;
|
|
80
|
+
const meta = slot == null ? void 0 : slot.visualMetadata;
|
|
81
|
+
if (!meta) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
return meta.backgroundColor || ((_meta$palette = meta.palette) == null ? void 0 : _meta$palette.dominant) || undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The aspect ratio to render `imageUrl` at. Normally the slot's requested
|
|
89
|
+
* ratio, but a contained crop (ADR 0220) deliberately returns another, and the
|
|
90
|
+
* URL's own `fill/w_,h_` carries that one — so honouring `crop.ratio` is what
|
|
91
|
+
* keeps the picture undistorted.
|
|
92
|
+
*/
|
|
93
|
+
export function renderRatioOf(slot, requestedRatio) {
|
|
94
|
+
const crop = slot == null ? void 0 : slot.crop;
|
|
95
|
+
if (crop != null && crop.contained && crop.ratio && crop.ratio > 0) {
|
|
96
|
+
return crop.ratio;
|
|
97
|
+
}
|
|
98
|
+
return requestedRatio;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=imageSetTypes.js.map
|