Skip to content

Commit 984af9c

Browse files
committed
Add MDXImage component
1 parent fdfeadc commit 984af9c

1 file changed

Lines changed: 132 additions & 0 deletions

File tree

src/components/MDXImage.astro

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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

Comments
 (0)