@@ -60,6 +60,7 @@ export type ImageUploadHandler = (
6060) => ImageUploadResult | null | Promise < ImageUploadResult | null > ;
6161
6262const DEFAULT_MAX_IMAGE_BYTES = 1_000_000 ;
63+ const UPLOADED_IMAGE_PRELOAD_TIMEOUT_MS = 8_000 ;
6364const UploadableImage = Image . extend ( {
6465 addAttributes ( ) {
6566 return {
@@ -499,6 +500,44 @@ export function Editor({
499500 reader . readAsDataURL ( file ) ;
500501 } ) ;
501502
503+ const preloadImageSource = async ( src : string , timeoutMs = UPLOADED_IMAGE_PRELOAD_TIMEOUT_MS ) : Promise < boolean > =>
504+ new Promise < boolean > ( ( resolve ) => {
505+ const image = new window . Image ( ) ;
506+ let settled = false ;
507+ const timeoutId = window . setTimeout ( ( ) => {
508+ if ( settled ) return ;
509+ settled = true ;
510+ image . onload = null ;
511+ image . onerror = null ;
512+ resolve ( false ) ;
513+ } , timeoutMs ) ;
514+
515+ const finish = ( ok : boolean ) : void => {
516+ if ( settled ) return ;
517+ settled = true ;
518+ window . clearTimeout ( timeoutId ) ;
519+ image . onload = null ;
520+ image . onerror = null ;
521+ resolve ( ok ) ;
522+ } ;
523+
524+ image . onerror = ( ) => finish ( false ) ;
525+ image . onload = ( ) => {
526+ if ( typeof image . decode === "function" ) {
527+ void image . decode ( ) . then (
528+ ( ) => finish ( true ) ,
529+ // decode errors can still have a usable image after load; keep it non-blocking.
530+ ( ) => finish ( true ) ,
531+ ) ;
532+ return ;
533+ }
534+ finish ( true ) ;
535+ } ;
536+
537+ image . src = src ;
538+ if ( image . complete && image . naturalWidth > 0 ) finish ( true ) ;
539+ } ) ;
540+
502541 const findImageNodeByUploadId = (
503542 uploadId : string ,
504543 ) : { pos : number ; attrs : UploadableImageAttrs } | null => {
@@ -590,6 +629,17 @@ export function Editor({
590629 return ;
591630 }
592631
632+ const preloaded = await preloadImageSource ( resolved . src ) ;
633+ if ( ! preloaded ) {
634+ finalizeImageUpload ( uploadId , ( attrs ) => ( {
635+ ...attrs ,
636+ uploading : false ,
637+ uploadError : "Image uploaded, but preview failed to load" ,
638+ } ) ) ;
639+ cleanupUpload ( uploadId , { revokeBlob : false } ) ;
640+ return ;
641+ }
642+
593643 finalizeImageUpload ( uploadId , ( attrs ) : UploadableImageAttrs | null => {
594644 const expectedBlob = expectedBlobByUploadIdRef . current . get ( uploadId ) ;
595645 const currentSrc = typeof attrs . src === "string" ? attrs . src : "" ;
0 commit comments