|
| 1 | +--- |
| 2 | +import { Image } from 'astro:assets'; |
| 3 | +
|
| 4 | +interface Props { |
| 5 | + src: string; |
| 6 | + alt?: string; |
| 7 | + width?: string | number; |
| 8 | + height?: string | number; |
| 9 | + style?: any; |
| 10 | + className?: string; |
| 11 | + [key: string]: any; |
| 12 | +} |
| 13 | +
|
| 14 | +const { src: rawSrc, alt, width, height, style, className, ...props } = Astro.props; |
| 15 | +
|
| 16 | +// Ensure src is a string |
| 17 | +const src = typeof rawSrc === 'string' ? rawSrc : (rawSrc?.default || rawSrc?.src || String(rawSrc || '')); |
| 18 | +
|
| 19 | +let imageSrc: ImageMetadata | undefined; |
| 20 | +const id = src.split('/').pop() || src; |
| 21 | +
|
| 22 | +// If src is already a full URL or a path that clearly looks already resolved by Vite (starts with /_astro/ or /@fs/), use it |
| 23 | +if (src.startsWith('http') || src.startsWith('/_astro/') || src.startsWith('/@fs/')) { |
| 24 | + // Return regular img for external or already resolved public paths |
| 25 | + imageSrc = undefined; |
| 26 | +} else { |
| 27 | + // 1. Load all images |
| 28 | + const allImages = import.meta.glob<{ default: ImageMetadata }>([ |
| 29 | + '/src/assets/**/*.{png,jpg,jpeg,webp,gif}', |
| 30 | + '/src/content/docs/**/*.{png,jpg,jpeg,webp,gif}' |
| 31 | + ], { eager: true }); |
| 32 | +
|
| 33 | + // 2. Determine article name from URL |
| 34 | + const currentPath = Astro.url.pathname; |
| 35 | + const pathSegments = currentPath.split('/').filter(Boolean); |
| 36 | + const articleName = pathSegments[pathSegments.length - 1] || 'index'; |
| 37 | +
|
| 38 | + if (id) { |
| 39 | + // Priority 1: Article-specific |
| 40 | + const articleSpecificPrefix = `/src/assets/${articleName}/`; |
| 41 | + for (const [path, module] of Object.entries(allImages)) { |
| 42 | + if (path.includes(articleSpecificPrefix) && path.split('/').pop() === id) { |
| 43 | + imageSrc = module.default; |
| 44 | + break; |
| 45 | + } |
| 46 | + } |
| 47 | +
|
| 48 | + // Priority 2: Shared |
| 49 | + if (!imageSrc) { |
| 50 | + const sharedPrefix = `/src/assets/shared/`; |
| 51 | + for (const [path, module] of Object.entries(allImages)) { |
| 52 | + if (path.includes(sharedPrefix) && path.split('/').pop() === id) { |
| 53 | + imageSrc = module.default; |
| 54 | + break; |
| 55 | + } |
| 56 | + } |
| 57 | + } |
| 58 | +
|
| 59 | + // Priority 3: Legacy |
| 60 | + if (!imageSrc) { |
| 61 | + const legacyFolders = ['img', 'FF_img', 'img_webhook_flows']; |
| 62 | + for (const folder of legacyFolders) { |
| 63 | + const legacyPathPart = `/src/content/docs/version-3.0/${folder}/`; |
| 64 | + for (const [path, module] of Object.entries(allImages)) { |
| 65 | + if (path.includes(legacyPathPart) && path.split('/').pop() === id) { |
| 66 | + imageSrc = module.default; |
| 67 | + break; |
| 68 | + } |
| 69 | + } |
| 70 | + if (imageSrc) break; |
| 71 | + } |
| 72 | + } |
| 73 | +
|
| 74 | + // Priority 4: Best match fallback |
| 75 | + if (!imageSrc) { |
| 76 | + let bestMatchScore = -1; |
| 77 | + for (const [path, module] of Object.entries(allImages)) { |
| 78 | + const filename = path.split('/').pop(); |
| 79 | + if (filename === id) { |
| 80 | + const imgParts = path.split('/').filter(Boolean); |
| 81 | + const urlParts = currentPath.split('/').filter(Boolean); |
| 82 | + let commonPathLength = 0; |
| 83 | + const minLength = Math.min(imgParts.length, urlParts.length); |
| 84 | + for (let i = 0; i < minLength; i++) { |
| 85 | + if (imgParts[i] === urlParts[i]) commonPathLength++; |
| 86 | + else break; |
| 87 | + } |
| 88 | + if (commonPathLength > bestMatchScore) { |
| 89 | + bestMatchScore = commonPathLength; |
| 90 | + imageSrc = module.default; |
| 91 | + } |
| 92 | + } |
| 93 | + } |
| 94 | + } |
| 95 | + } |
| 96 | +} |
| 97 | +
|
| 98 | +// Convert width/height to numbers if they are strings like "700px" |
| 99 | +const resolvedWidth = typeof width === 'string' ? parseInt(width.replace('px', '')) : width; |
| 100 | +const resolvedHeight = typeof height === 'string' ? parseInt(height.replace('px', '')) : height; |
| 101 | +const finalWidth = resolvedWidth ?? imageSrc?.width; |
| 102 | +const finalHeight = resolvedHeight ?? imageSrc?.height; |
| 103 | +
|
| 104 | +// Merge styles |
| 105 | +const finalStyle = { |
| 106 | + maxWidth: '100%', |
| 107 | + height: 'auto', |
| 108 | + ...(typeof style === 'object' ? style : {}), |
| 109 | +}; |
| 110 | +
|
| 111 | +// If width is provided in props, use it in style too |
| 112 | +if (width) { |
| 113 | + finalStyle.width = typeof width === 'number' ? `${width}px` : width; |
| 114 | +} |
| 115 | +--- |
| 116 | + |
| 117 | +{imageSrc ? ( |
| 118 | + <Image |
| 119 | + src={imageSrc} |
| 120 | + alt={alt || id || ''} |
| 121 | + width={finalWidth} |
| 122 | + height={finalHeight} |
| 123 | + class:list={[ |
| 124 | + "zoom-image", |
| 125 | + className |
| 126 | + ]} |
| 127 | + style={finalStyle} |
| 128 | + {...props} |
| 129 | + /> |
| 130 | +) : ( |
| 131 | + <img src={src} alt={alt} style={finalStyle} class={className} {...props} /> |
| 132 | +)} |
0 commit comments