1+ /**
2+ * Custom IdealImage component with improved quality for sized images
3+ */
4+
5+ import React from 'react' ;
6+ import ReactIdealImage from '@slorber/react-ideal-image' ;
7+ import { translate } from '@docusaurus/Translate' ;
8+
9+ // Helper function to convert bytes to human readable size
10+ function bytesToSize ( bytes ) {
11+ const sizes = [ 'B' , 'KB' , 'MB' , 'GB' , 'TB' ] ;
12+ if ( bytes === 0 ) {
13+ return 'n/a' ;
14+ }
15+ const scale = Math . floor ( Math . log ( bytes ) / Math . log ( 1024 ) ) ;
16+ if ( scale === 0 ) {
17+ return `${ bytes } ${ sizes [ scale ] } ` ;
18+ }
19+ return `${ ( bytes / 1024 ** scale ) . toFixed ( 1 ) } ${ sizes [ scale ] } ` ;
20+ }
21+
22+ // Custom message handler
23+ function getMessage ( icon , state ) {
24+ switch ( icon ) {
25+ case 'noicon' :
26+ case 'loaded' :
27+ return null ;
28+ case 'loading' :
29+ return translate ( {
30+ id : 'theme.IdealImageMessage.loading' ,
31+ message : 'Loading...' ,
32+ description : 'When the full-scale image is loading' ,
33+ } ) ;
34+ case 'load' : {
35+ const { pickedSrc} = state ;
36+ const { size} = pickedSrc ;
37+ const sizeMessage = size ? ` (${ bytesToSize ( size ) } )` : '' ;
38+ return translate (
39+ {
40+ id : 'theme.IdealImageMessage.load' ,
41+ message : 'Click to load{sizeMessage}' ,
42+ description :
43+ 'To prompt users to load the full image. sizeMessage is a parenthesized size figure.' ,
44+ } ,
45+ { sizeMessage} ,
46+ ) ;
47+ }
48+ case 'offline' :
49+ return translate ( {
50+ id : 'theme.IdealImageMessage.offline' ,
51+ message : 'Your browser is offline. Image not loaded' ,
52+ description : 'When the user is viewing an offline document' ,
53+ } ) ;
54+ case 'error' : {
55+ const { loadInfo} = state ;
56+ if ( loadInfo === 404 ) {
57+ return translate ( {
58+ id : 'theme.IdealImageMessage.404error' ,
59+ message : '404. Image not found' ,
60+ description : 'When the image is not found' ,
61+ } ) ;
62+ }
63+ return translate ( {
64+ id : 'theme.IdealImageMessage.error' ,
65+ message : 'Error. Click to reload' ,
66+ description : 'When the image fails to load for unknown error' ,
67+ } ) ;
68+ }
69+ default :
70+ throw new Error ( `Wrong icon: ${ icon } ` ) ;
71+ }
72+ }
73+
74+ export default function IdealImage ( props ) {
75+ const { img, size, ...propsRest } = props ;
76+
77+ // In dev env just use regular img with original file
78+ if ( typeof img === 'string' || 'default' in img ) {
79+ return (
80+ < img src = { typeof img === 'string' ? img : img . default } { ...propsRest } />
81+ ) ;
82+ }
83+
84+ // For sized images (sm, md), use a higher quality placeholder
85+ // to avoid the blurry initial display
86+ let betterPlaceholder = img . preSrc ;
87+
88+ if ( size === 'sm' || size === 'md' || size === 'lg' ) {
89+ const availableImages = img . src . images || [ ] ;
90+ if ( availableImages . length > 0 ) {
91+ // For smaller sizes, use 600px image as placeholder instead of LQIP
92+ // This provides much better initial quality
93+ const higherQualityImage = availableImages . find ( image => image . width === 600 ) ||
94+ availableImages . find ( image => image . width === 300 ) ||
95+ availableImages [ availableImages . length - 1 ] ;
96+
97+ if ( higherQualityImage ) {
98+ betterPlaceholder = higherQualityImage . path ;
99+ }
100+ }
101+ }
102+
103+ return (
104+ < ReactIdealImage
105+ { ...propsRest }
106+ height = { img . src . height ?? 100 }
107+ width = { img . src . width ?? 100 }
108+ placeholder = { { lqip : betterPlaceholder } }
109+ src = { img . src . src }
110+ srcSet = { img . src . images . map ( ( image ) => ( {
111+ ...image ,
112+ src : image . path ,
113+ } ) ) }
114+ getMessage = { getMessage }
115+ // For sized images, load eagerly to show quality immediately
116+ threshold = { size ? 0 : undefined }
117+ />
118+ ) ;
119+ }
0 commit comments