@wix/web5-core 1.63.21 → 1.63.23
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 +12 -30
- package/dist/cjs/components/ui/OptimizedImage.js.map +1 -1
- package/dist/cjs/entity/entityHref.js +53 -0
- package/dist/cjs/entity/entityHref.js.map +1 -0
- package/dist/cjs/entity/entityPayload.js +58 -4
- package/dist/cjs/entity/entityPayload.js.map +1 -1
- package/dist/cjs/entity/index.js +5 -2
- package/dist/cjs/entity/index.js.map +1 -1
- package/dist/cjs/index.js +5 -16
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/dependencies.js.map +1 -1
- package/dist/esm/components/ui/OptimizedImage.js +3 -21
- package/dist/esm/components/ui/OptimizedImage.js.map +1 -1
- package/dist/esm/entity/entityHref.js +49 -0
- package/dist/esm/entity/entityHref.js.map +1 -0
- package/dist/esm/entity/entityPayload.js +57 -4
- package/dist/esm/entity/entityPayload.js.map +1 -1
- package/dist/esm/entity/index.js +2 -1
- package/dist/esm/entity/index.js.map +1 -1
- package/dist/esm/index.js +1 -9
- 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 +0 -12
- package/dist/types/components/ui/OptimizedImage.d.ts.map +1 -1
- package/dist/types/entity/entityHref.d.ts +39 -0
- package/dist/types/entity/entityHref.d.ts.map +1 -0
- package/dist/types/entity/entityPayload.d.ts +34 -0
- package/dist/types/entity/entityPayload.d.ts.map +1 -1
- package/dist/types/entity/index.d.ts +2 -1
- package/dist/types/entity/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types/dependencies.d.ts +0 -15
- package/dist/types/types/dependencies.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/cjs/context/ImageSetContext.js +0 -234
- package/dist/cjs/context/ImageSetContext.js.map +0 -1
- package/dist/cjs/image/imageSetTypes.js +0 -108
- package/dist/cjs/image/imageSetTypes.js.map +0 -1
- package/dist/esm/context/ImageSetContext.js +0 -221
- package/dist/esm/context/ImageSetContext.js.map +0 -1
- package/dist/esm/image/imageSetTypes.js +0 -100
- package/dist/esm/image/imageSetTypes.js.map +0 -1
- package/dist/types/context/ImageSetContext.d.ts +0 -74
- package/dist/types/context/ImageSetContext.d.ts.map +0 -1
- package/dist/types/image/imageSetTypes.d.ts +0 -185
- package/dist/types/image/imageSetTypes.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
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":[]}
|
|
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":"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","ignoreList":[]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The url an entity card links to.
|
|
3
|
+
*
|
|
4
|
+
* Every entity surface was carrying its own copy of this rule — three of them
|
|
5
|
+
* byte-identical (`EntityProductListSection`, `EntityCollectionSection`,
|
|
6
|
+
* `ComparisonSection`), two more spelled inline (`EntityArticleListSection`,
|
|
7
|
+
* `HeroEntityImageSection`) — in every client bundle. It lives here for the
|
|
8
|
+
* same reason `formatProductPriceLabel` does (DL #218): every seed template
|
|
9
|
+
* externalizes `@wix/web5-core`, so the card, the hero and the comparison
|
|
10
|
+
* surface all get one implementation and no template needs a change.
|
|
11
|
+
*
|
|
12
|
+
* ## It picks; it does not rewrite
|
|
13
|
+
*
|
|
14
|
+
* Deciding WHICH url an item links to is a property of the item, and this
|
|
15
|
+
* knows the item. Deciding what ORIGIN that url should be on is a property of
|
|
16
|
+
* the surface — it needs the storefront the shopper is standing on and the set
|
|
17
|
+
* of origins that count as this site's — and this knows neither. That belongs
|
|
18
|
+
* to the link layer (`useWeb5Link().resolveUrl`), which has both.
|
|
19
|
+
*
|
|
20
|
+
* So the contract is one line: `resolveUrl(entityHref(item))`. One picker, one
|
|
21
|
+
* resolver, nothing in between. A second rewriter sitting between them is how
|
|
22
|
+
* you get two authorities on the same href fighting over it.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** An entity reference that never resolved — linking it navigates nowhere. */
|
|
26
|
+
const UNRESOLVED_SCHEME = 'web5://';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The url an entity links to, or `undefined` when it has none worth linking.
|
|
30
|
+
*
|
|
31
|
+
* `data.url` is the resolved catalog url and wins: it is the row the turn sent,
|
|
32
|
+
* so on a variant question it is the matched child's url — the one carrying
|
|
33
|
+
* `?variant=` (DL #216). `entityUrl` is what parsing recovered from the
|
|
34
|
+
* markdown link and is the fallback for an item resolution never reached.
|
|
35
|
+
*
|
|
36
|
+
* A `web5://` url means the entity resolved to nothing. The caller renders the
|
|
37
|
+
* card unlinked rather than linking a scheme the browser cannot follow — which
|
|
38
|
+
* is what every copy of this rule already did, and worth keeping central so it
|
|
39
|
+
* cannot drift on one surface.
|
|
40
|
+
*/
|
|
41
|
+
export function entityHref(item) {
|
|
42
|
+
var _item$data;
|
|
43
|
+
const url = ((_item$data = item.data) == null ? void 0 : _item$data.url) || item.entityUrl || undefined;
|
|
44
|
+
if (!url || url.startsWith(UNRESOLVED_SCHEME)) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return url;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=entityHref.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["UNRESOLVED_SCHEME","entityHref","item","_item$data","url","data","entityUrl","undefined","startsWith"],"sources":["../../../src/entity/entityHref.ts"],"sourcesContent":["/**\n * The url an entity card links to.\n *\n * Every entity surface was carrying its own copy of this rule — three of them\n * byte-identical (`EntityProductListSection`, `EntityCollectionSection`,\n * `ComparisonSection`), two more spelled inline (`EntityArticleListSection`,\n * `HeroEntityImageSection`) — in every client bundle. It lives here for the\n * same reason `formatProductPriceLabel` does (DL #218): every seed template\n * externalizes `@wix/web5-core`, so the card, the hero and the comparison\n * surface all get one implementation and no template needs a change.\n *\n * ## It picks; it does not rewrite\n *\n * Deciding WHICH url an item links to is a property of the item, and this\n * knows the item. Deciding what ORIGIN that url should be on is a property of\n * the surface — it needs the storefront the shopper is standing on and the set\n * of origins that count as this site's — and this knows neither. That belongs\n * to the link layer (`useWeb5Link().resolveUrl`), which has both.\n *\n * So the contract is one line: `resolveUrl(entityHref(item))`. One picker, one\n * resolver, nothing in between. A second rewriter sitting between them is how\n * you get two authorities on the same href fighting over it.\n */\nimport type { EntityItem } from '../component/types';\n\n/** An entity reference that never resolved — linking it navigates nowhere. */\nconst UNRESOLVED_SCHEME = 'web5://';\n\n/**\n * The url an entity links to, or `undefined` when it has none worth linking.\n *\n * `data.url` is the resolved catalog url and wins: it is the row the turn sent,\n * so on a variant question it is the matched child's url — the one carrying\n * `?variant=` (DL #216). `entityUrl` is what parsing recovered from the\n * markdown link and is the fallback for an item resolution never reached.\n *\n * A `web5://` url means the entity resolved to nothing. The caller renders the\n * card unlinked rather than linking a scheme the browser cannot follow — which\n * is what every copy of this rule already did, and worth keeping central so it\n * cannot drift on one surface.\n */\nexport function entityHref(item: EntityItem): string | undefined {\n const url = item.data?.url || item.entityUrl || undefined;\n if (!url || url.startsWith(UNRESOLVED_SCHEME)) {\n return undefined;\n }\n return url;\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA;AACA,MAAMA,iBAAiB,GAAG,SAAS;;AAEnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,UAAUA,CAACC,IAAgB,EAAsB;EAAA,IAAAC,UAAA;EAC/D,MAAMC,GAAG,GAAG,EAAAD,UAAA,GAAAD,IAAI,CAACG,IAAI,qBAATF,UAAA,CAAWC,GAAG,KAAIF,IAAI,CAACI,SAAS,IAAIC,SAAS;EACzD,IAAI,CAACH,GAAG,IAAIA,GAAG,CAACI,UAAU,CAACR,iBAAiB,CAAC,EAAE;IAC7C,OAAOO,SAAS;EAClB;EACA,OAAOH,GAAG;AACZ","ignoreList":[]}
|
|
@@ -1,3 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A catalog url reduced to `path + query + hash`, so it resolves against the
|
|
3
|
+
* storefront the shopper is actually on.
|
|
4
|
+
*
|
|
5
|
+
* A site's index stores each entity's url from the mapping schema's
|
|
6
|
+
* `payload.url` template, and a live site writes that template ABSOLUTE —
|
|
7
|
+
* `https://www.smallflower.com/products/{handle}`. A demo, staging or clone
|
|
8
|
+
* store serves the same catalog from a different origin, so rendered as
|
|
9
|
+
* indexed, every product link in an answer walks the shopper off the store
|
|
10
|
+
* they are on and onto the canonical one — a different cart, a different
|
|
11
|
+
* inventory, a different checkout.
|
|
12
|
+
*
|
|
13
|
+
* The quieter half is worse. `useWeb5Link.appendWeb5Id` classifies such a url
|
|
14
|
+
* as external and, by design, refuses to stamp `?web5_id=` on an external
|
|
15
|
+
* link, so the destination page never learns which conversation sent the
|
|
16
|
+
* shopper there and every warm-guarded placement on it stays inert.
|
|
17
|
+
*
|
|
18
|
+
* The origin is the one thing in the row the index cannot know and the client
|
|
19
|
+
* can: an entity url names a resource in THIS site's catalog, and the catalog
|
|
20
|
+
* is served by the storefront the page is running on. So the path is
|
|
21
|
+
* authoritative and the origin is advisory. `linkConfig.baseUrl` remains the
|
|
22
|
+
* escape hatch — a client that means its links to leave the current origin
|
|
23
|
+
* says so there, and `resolveRelativeUrl` puts that base back on.
|
|
24
|
+
*
|
|
25
|
+
* Only `url` is reduced. Images legitimately live on another origin
|
|
26
|
+
* (`cdn.shopify.com`), and `img` is left exactly as indexed.
|
|
27
|
+
*
|
|
28
|
+
* This is the client's half. The mapping schema is where it should be
|
|
29
|
+
* corrected — the reference schema in `web5-collection-mapping/schema/
|
|
30
|
+
* examples/ecom-site.schema.json` already ships the right shape
|
|
31
|
+
* (`{"template": "/products/{handle}"}`) — and a site whose schema is already
|
|
32
|
+
* relative passes through here untouched, so the two do not fight.
|
|
33
|
+
*/
|
|
34
|
+
export function toCatalogPath(url) {
|
|
35
|
+
// Not absolute http(s): a root-relative path is already right, and a
|
|
36
|
+
// `mailto:`/`web5://`/protocol-relative url is not a catalog path at all.
|
|
37
|
+
if (!/^https?:\/\//i.test(url)) {
|
|
38
|
+
return url;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const parsed = new URL(url);
|
|
42
|
+
// `?variant=…` is exactly how a resolved variant travels (DL #216), and
|
|
43
|
+
// the hash can carry an anchor the answer meant — both survive.
|
|
44
|
+
return parsed.pathname + parsed.search + parsed.hash;
|
|
45
|
+
} catch {
|
|
46
|
+
return url;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
1
50
|
/**
|
|
2
51
|
* One entity row, normalized onto the `ApiPayloadItem` shape both resolution
|
|
3
52
|
* paths key off.
|
|
@@ -28,15 +77,19 @@ export function normalizeEntityItem(raw) {
|
|
|
28
77
|
}
|
|
29
78
|
|
|
30
79
|
// Rows travel as-is apart from the `doc_id` → `id` alias every consumer keys
|
|
31
|
-
// off, and the image alias
|
|
80
|
+
// off, the origin reduction below, and the image alias after it.
|
|
32
81
|
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
82
|
+
// The url is never REBUILT — rebuilding the path from `handle` would throw
|
|
83
|
+
// away the `?variant=…` that is exactly how a resolved variant travels.
|
|
84
|
+
// `toCatalogPath` only drops the origin the index stamped; path, query and
|
|
85
|
+
// hash are carried over verbatim.
|
|
36
86
|
const normalized = {
|
|
37
87
|
...rec,
|
|
38
88
|
id
|
|
39
89
|
};
|
|
90
|
+
if (typeof rec.url === 'string' && rec.url) {
|
|
91
|
+
normalized.url = toCatalogPath(rec.url);
|
|
92
|
+
}
|
|
40
93
|
|
|
41
94
|
// Product rows carry the image as `thumbnail` / `images[]` (the Vespa doc
|
|
42
95
|
// shape), but the shared transform (`transformToGenericEntityData`) reads
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["normalizeEntityItem","raw","Array","isArray","rec","docId","doc_id","ownId","id","normalized","img","thumbnail","images","entityPayloadFromItems","container","items","payload","item","push"],"sources":["../../../src/entity/entityPayload.ts"],"sourcesContent":["import type { ApiPayloadItem } from '../types/entity';\n\n/**\n * One entity row, normalized onto the `ApiPayloadItem` shape both resolution\n * paths key off.\n *\n * Two producers, one document. `list-items` answers with the raw retrieval\n * document; the turn answers with the engine's allowlisted projection of the\n * SAME document — same field names, plus the keys only a card reads (`type`,\n * `variantId`, and the matched child's `url`/`img`/`price` already overlaid).\n * Normalizing both here is what lets `findEntityInPayload` resolve a reference\n * without caring which one answered.\n *\n * `id` is what a `web5://entity/<type>/<id>` reference resolves against, and\n * the reference carries the DOCUMENT id — so `doc_id` becomes `id`, exactly as\n * the `list-items` path has always done. The fallback to a row's own `id` is\n * for the day a store publishes one (none does today); a row with neither has\n * nothing to be keyed by and is dropped.\n */\nexport function normalizeEntityItem(raw: unknown): ApiPayloadItem | null {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n return null;\n }\n const rec = raw as Record<string, unknown>;\n const docId = typeof rec.doc_id === 'string' ? rec.doc_id : '';\n const ownId = typeof rec.id === 'string' ? rec.id : '';\n const id = docId || ownId;\n if (!id) {\n return null;\n }\n\n // Rows travel as-is apart from the `doc_id` → `id` alias every consumer keys\n // off, and the image alias
|
|
1
|
+
{"version":3,"names":["toCatalogPath","url","test","parsed","URL","pathname","search","hash","normalizeEntityItem","raw","Array","isArray","rec","docId","doc_id","ownId","id","normalized","img","thumbnail","images","entityPayloadFromItems","container","items","payload","item","push"],"sources":["../../../src/entity/entityPayload.ts"],"sourcesContent":["import type { ApiPayloadItem } from '../types/entity';\n\n/**\n * A catalog url reduced to `path + query + hash`, so it resolves against the\n * storefront the shopper is actually on.\n *\n * A site's index stores each entity's url from the mapping schema's\n * `payload.url` template, and a live site writes that template ABSOLUTE —\n * `https://www.smallflower.com/products/{handle}`. A demo, staging or clone\n * store serves the same catalog from a different origin, so rendered as\n * indexed, every product link in an answer walks the shopper off the store\n * they are on and onto the canonical one — a different cart, a different\n * inventory, a different checkout.\n *\n * The quieter half is worse. `useWeb5Link.appendWeb5Id` classifies such a url\n * as external and, by design, refuses to stamp `?web5_id=` on an external\n * link, so the destination page never learns which conversation sent the\n * shopper there and every warm-guarded placement on it stays inert.\n *\n * The origin is the one thing in the row the index cannot know and the client\n * can: an entity url names a resource in THIS site's catalog, and the catalog\n * is served by the storefront the page is running on. So the path is\n * authoritative and the origin is advisory. `linkConfig.baseUrl` remains the\n * escape hatch — a client that means its links to leave the current origin\n * says so there, and `resolveRelativeUrl` puts that base back on.\n *\n * Only `url` is reduced. Images legitimately live on another origin\n * (`cdn.shopify.com`), and `img` is left exactly as indexed.\n *\n * This is the client's half. The mapping schema is where it should be\n * corrected — the reference schema in `web5-collection-mapping/schema/\n * examples/ecom-site.schema.json` already ships the right shape\n * (`{\"template\": \"/products/{handle}\"}`) — and a site whose schema is already\n * relative passes through here untouched, so the two do not fight.\n */\nexport function toCatalogPath(url: string): string {\n // Not absolute http(s): a root-relative path is already right, and a\n // `mailto:`/`web5://`/protocol-relative url is not a catalog path at all.\n if (!/^https?:\\/\\//i.test(url)) {\n return url;\n }\n try {\n const parsed = new URL(url);\n // `?variant=…` is exactly how a resolved variant travels (DL #216), and\n // the hash can carry an anchor the answer meant — both survive.\n return parsed.pathname + parsed.search + parsed.hash;\n } catch {\n return url;\n }\n}\n\n/**\n * One entity row, normalized onto the `ApiPayloadItem` shape both resolution\n * paths key off.\n *\n * Two producers, one document. `list-items` answers with the raw retrieval\n * document; the turn answers with the engine's allowlisted projection of the\n * SAME document — same field names, plus the keys only a card reads (`type`,\n * `variantId`, and the matched child's `url`/`img`/`price` already overlaid).\n * Normalizing both here is what lets `findEntityInPayload` resolve a reference\n * without caring which one answered.\n *\n * `id` is what a `web5://entity/<type>/<id>` reference resolves against, and\n * the reference carries the DOCUMENT id — so `doc_id` becomes `id`, exactly as\n * the `list-items` path has always done. The fallback to a row's own `id` is\n * for the day a store publishes one (none does today); a row with neither has\n * nothing to be keyed by and is dropped.\n */\nexport function normalizeEntityItem(raw: unknown): ApiPayloadItem | null {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n return null;\n }\n const rec = raw as Record<string, unknown>;\n const docId = typeof rec.doc_id === 'string' ? rec.doc_id : '';\n const ownId = typeof rec.id === 'string' ? rec.id : '';\n const id = docId || ownId;\n if (!id) {\n return null;\n }\n\n // Rows travel as-is apart from the `doc_id` → `id` alias every consumer keys\n // off, the origin reduction below, and the image alias after it.\n //\n // The url is never REBUILT — rebuilding the path from `handle` would throw\n // away the `?variant=…` that is exactly how a resolved variant travels.\n // `toCatalogPath` only drops the origin the index stamped; path, query and\n // hash are carried over verbatim.\n const normalized: Record<string, unknown> = { ...rec, id };\n\n if (typeof rec.url === 'string' && rec.url) {\n normalized.url = toCatalogPath(rec.url);\n }\n\n // Product rows carry the image as `thumbnail` / `images[]` (the Vespa doc\n // shape), but the shared transform (`transformToGenericEntityData`) reads\n // `img`. Alias it so product cards keep their image.\n //\n // A bridge, not a contract: a row that already carries `img` — a CMS doc, or\n // one where retrieval overlaid a matched child's presentation fields — passes\n // through untouched. Drop the alias once every producer emits `img`.\n if (normalized.img == null) {\n if (typeof rec.thumbnail === 'string' && rec.thumbnail) {\n normalized.img = rec.thumbnail;\n } else if (\n Array.isArray(rec.images) &&\n typeof rec.images[0] === 'string' &&\n rec.images[0]\n ) {\n normalized.img = rec.images[0];\n }\n }\n\n return normalized as unknown as ApiPayloadItem;\n}\n\n/**\n * Read an `{ items: [...] }` container into a normalized payload.\n *\n * Both carriers of the turn's resolved entities use that shape: the live\n * stream's `entities` frame (`TurnEntities.items`) and the persisted\n * `additional_data[\"documents\"]` the reload path reads back. A bare array is\n * accepted too, so a caller that already unwrapped one can pass it straight in.\n *\n * Anything that isn't a keyable row is dropped rather than passed on — a\n * payload entry with no id can only ever fail to match.\n */\nexport function entityPayloadFromItems(container: unknown): ApiPayloadItem[] {\n let items: unknown;\n if (Array.isArray(container)) {\n items = container;\n } else if (container && typeof container === 'object') {\n items = (container as { items?: unknown }).items;\n }\n if (!Array.isArray(items)) {\n return [];\n }\n\n const payload: ApiPayloadItem[] = [];\n for (const item of items) {\n const normalized = normalizeEntityItem(item);\n if (normalized) {\n payload.push(normalized);\n }\n }\n return payload;\n}\n"],"mappings":"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","ignoreList":[]}
|
package/dist/esm/entity/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { defaultExtractor } from './defaultExtractor.js';
|
|
2
2
|
export { defaultExtractor, transformToSolutionEntityData, transformToBlogPostEntityData, transformToGenericEntityData } from './defaultExtractor.js';
|
|
3
3
|
export { enrichEntitiesFromPayload } from './enrichEntitiesFromPayload.js';
|
|
4
|
-
export {
|
|
4
|
+
export { entityHref } from './entityHref.js';
|
|
5
|
+
export { normalizeEntityItem, entityPayloadFromItems, toCatalogPath } from './entityPayload.js';
|
|
5
6
|
export { isProductFamilyEntityType } from './entityFamily.js';
|
|
6
7
|
export { mergeEntityData } from './mergeEntityData.js';
|
|
7
8
|
export { toMatchedOptions, formatMoney, readCurrencyCode, formatPriceField, formatProductPriceLabel } from './matchedVariant.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["defaultExtractor","transformToSolutionEntityData","transformToBlogPostEntityData","transformToGenericEntityData","enrichEntitiesFromPayload","normalizeEntityItem","entityPayloadFromItems","isProductFamilyEntityType","mergeEntityData","toMatchedOptions","formatMoney","readCurrencyCode","formatPriceField","formatProductPriceLabel","fetchEntityListData","listEntityItems","LIST_ITEMS_ENDPOINT","extractorRegistry","getEntityExtractor","clientId","registerEntityExtractor","extractor"],"sources":["../../../src/entity/index.ts"],"sourcesContent":["import type { EntityExtractor } from './types';\nimport { defaultExtractor } from './defaultExtractor';\n\nexport {\n defaultExtractor,\n transformToSolutionEntityData,\n transformToBlogPostEntityData,\n transformToGenericEntityData,\n} from './defaultExtractor';\n\nexport { enrichEntitiesFromPayload } from './enrichEntitiesFromPayload';\nexport { normalizeEntityItem
|
|
1
|
+
{"version":3,"names":["defaultExtractor","transformToSolutionEntityData","transformToBlogPostEntityData","transformToGenericEntityData","enrichEntitiesFromPayload","entityHref","normalizeEntityItem","entityPayloadFromItems","toCatalogPath","isProductFamilyEntityType","mergeEntityData","toMatchedOptions","formatMoney","readCurrencyCode","formatPriceField","formatProductPriceLabel","fetchEntityListData","listEntityItems","LIST_ITEMS_ENDPOINT","extractorRegistry","getEntityExtractor","clientId","registerEntityExtractor","extractor"],"sources":["../../../src/entity/index.ts"],"sourcesContent":["import type { EntityExtractor } from './types';\nimport { defaultExtractor } from './defaultExtractor';\n\nexport {\n defaultExtractor,\n transformToSolutionEntityData,\n transformToBlogPostEntityData,\n transformToGenericEntityData,\n} from './defaultExtractor';\n\nexport { enrichEntitiesFromPayload } from './enrichEntitiesFromPayload';\nexport { entityHref } from './entityHref';\nexport {\n normalizeEntityItem,\n entityPayloadFromItems,\n toCatalogPath,\n} from './entityPayload';\nexport { isProductFamilyEntityType } from './entityFamily';\nexport { mergeEntityData } from './mergeEntityData';\nexport {\n toMatchedOptions,\n formatMoney,\n readCurrencyCode,\n formatPriceField,\n formatProductPriceLabel,\n type MatchedOption,\n type ProductPriceFields,\n} from './matchedVariant';\nexport {\n fetchEntityListData,\n type FetchEntityListDataOptions,\n} from './fetchEntityListData';\nexport {\n listEntityItems,\n LIST_ITEMS_ENDPOINT,\n type ListEntityItemsOptions,\n} from './listEntityItems';\n\nexport type { EntityExtractor } from './types';\n\n/**\n * Registry of client-specific entity extractors.\n * Only needed if a client requires custom extraction logic beyond the config-driven default.\n */\nconst extractorRegistry: Record<string, EntityExtractor> = {};\n\n/**\n * Get the entity extractor for a specific client.\n * The default extractor is config-driven and works for any client with entityConfig.\n */\nexport function getEntityExtractor(clientId?: string): EntityExtractor {\n if (clientId && extractorRegistry[clientId]) {\n return extractorRegistry[clientId];\n }\n return defaultExtractor;\n}\n\n/**\n * Register a custom entity extractor for a client\n * Useful for runtime registration or testing\n */\nexport function registerEntityExtractor(\n clientId: string,\n extractor: EntityExtractor,\n): void {\n extractorRegistry[clientId] = extractor;\n}\n"],"mappings":"AACA,SAASA,gBAAgB,QAAQ,oBAAoB;AAErD,SACEA,gBAAgB,EAChBC,6BAA6B,EAC7BC,6BAA6B,EAC7BC,4BAA4B,QACvB,oBAAoB;AAE3B,SAASC,yBAAyB,QAAQ,6BAA6B;AACvE,SAASC,UAAU,QAAQ,cAAc;AACzC,SACEC,mBAAmB,EACnBC,sBAAsB,EACtBC,aAAa,QACR,iBAAiB;AACxB,SAASC,yBAAyB,QAAQ,gBAAgB;AAC1D,SAASC,eAAe,QAAQ,mBAAmB;AACnD,SACEC,gBAAgB,EAChBC,WAAW,EACXC,gBAAgB,EAChBC,gBAAgB,EAChBC,uBAAuB,QAGlB,kBAAkB;AACzB,SACEC,mBAAmB,QAEd,uBAAuB;AAC9B,SACEC,eAAe,EACfC,mBAAmB,QAEd,mBAAmB;AAI1B;AACA;AACA;AACA;AACA,MAAMC,iBAAkD,GAAG,CAAC,CAAC;;AAE7D;AACA;AACA;AACA;AACA,OAAO,SAASC,kBAAkBA,CAACC,QAAiB,EAAmB;EACrE,IAAIA,QAAQ,IAAIF,iBAAiB,CAACE,QAAQ,CAAC,EAAE;IAC3C,OAAOF,iBAAiB,CAACE,QAAQ,CAAC;EACpC;EACA,OAAOrB,gBAAgB;AACzB;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASsB,uBAAuBA,CACrCD,QAAgB,EAChBE,SAA0B,EACpB;EACNJ,iBAAiB,CAACE,QAAQ,CAAC,GAAGE,SAAS;AACzC","ignoreList":[]}
|
package/dist/esm/index.js
CHANGED
|
@@ -18,11 +18,6 @@ export { DIAGNOSTIC_TYPES } from './component/diagnosticTypes.js';
|
|
|
18
18
|
// Image search filters
|
|
19
19
|
export { buildImageSearchFilter } from './image/imageSearchFilterTypes.js';
|
|
20
20
|
export { ImageSearchFilterToken, backgroundFilter } from './image/imageSearchFilters.js';
|
|
21
|
-
|
|
22
|
-
// Image sets — the page-level slot contract (ADR 0221)
|
|
23
|
-
|
|
24
|
-
export { MAX_IMAGE_SLOTS_PER_SET, DEFAULT_RENDER_WIDTH_PX, MAX_RENDER_WIDTH_PX, hasSlotImage, isSubjectMatch, backdropColorOf, renderRatioOf } from './image/imageSetTypes.js';
|
|
25
|
-
|
|
26
21
|
// Section definition classes + createSdkRegistry
|
|
27
22
|
export { HeroSectionDefinition, HeroEntitySectionDefinition, KpiSectionDefinition, FeatureCardsSectionDefinition, EntitySectionDefinition, EntityCollectionSectionDefinition, ComparisonSectionDefinition, TextBlockSectionDefinition, CtaBannerSectionDefinition, CalloutSectionDefinition, NextStepsSectionDefinition, FeatureSection9PlusDefinition, ListItemsSectionDefinition, SkipNodesSectionDefinition, HtmlCommentSectionDefinition, SearchSectionDefinition, ErrorSectionDefinition, FallbackSectionDefinition, createSdkRegistry } from './component/componentDefinitions/index.js';
|
|
28
23
|
|
|
@@ -59,15 +54,12 @@ export { ComponentDependenciesProvider, useComponentDependencies } from './conte
|
|
|
59
54
|
// Raw user query for the current response page
|
|
60
55
|
export { UserQueryProvider, useUserQuery } from './context/UserQueryContext.js';
|
|
61
56
|
|
|
62
|
-
// Page-level image slot collector
|
|
63
|
-
export { ImageSetProvider, useImageSlot, useImageSetEnabled } from './context/ImageSetContext.js';
|
|
64
|
-
|
|
65
57
|
// Chips context (suggestion chips shared between SearchSection and error states)
|
|
66
58
|
export { ChipsProvider, useChips } from './context/ChipsContext.js';
|
|
67
59
|
|
|
68
60
|
// DI types
|
|
69
61
|
|
|
70
|
-
export { defaultExtractor, enrichEntitiesFromPayload, normalizeEntityItem, entityPayloadFromItems, mergeEntityData, fetchEntityListData, listEntityItems, LIST_ITEMS_ENDPOINT, getEntityExtractor, registerEntityExtractor, transformToSolutionEntityData, transformToBlogPostEntityData, transformToGenericEntityData, toMatchedOptions, formatMoney, readCurrencyCode, formatPriceField, formatProductPriceLabel, isProductFamilyEntityType } from './entity/index.js';
|
|
62
|
+
export { defaultExtractor, enrichEntitiesFromPayload, entityHref, normalizeEntityItem, toCatalogPath, entityPayloadFromItems, mergeEntityData, fetchEntityListData, listEntityItems, LIST_ITEMS_ENDPOINT, getEntityExtractor, registerEntityExtractor, transformToSolutionEntityData, transformToBlogPostEntityData, transformToGenericEntityData, toMatchedOptions, formatMoney, readCurrencyCode, formatPriceField, formatProductPriceLabel, isProductFamilyEntityType } from './entity/index.js';
|
|
71
63
|
export { CALLOUT_KINDS, CALLOUT_SEMANTICS } from './types/callout.js';
|
|
72
64
|
|
|
73
65
|
// Wrapper hooks
|