@@ -844,7 +844,17 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
844844 relativePath : body . relativePath ,
845845 path,
846846 } ) ;
847- const MAX_READ_SIZE = 1_048_576 ; // 1MB
847+ const MAX_READ_SIZE_TEXT = 1_048_576 ; // 1MB
848+ const MAX_READ_SIZE_IMAGE = 10_485_760 ; // 10MB
849+ const IMAGE_EXTENSIONS_SET = new Set ( [
850+ ".png" , ".jpg" , ".jpeg" , ".gif" , ".bmp" , ".webp" ,
851+ ".ico" , ".tiff" , ".tif" , ".avif" , ".svg" ,
852+ ] ) ;
853+ const fileExt = target . relativePath
854+ . substring ( target . relativePath . lastIndexOf ( "." ) )
855+ . toLowerCase ( ) ;
856+ const isImageExt = IMAGE_EXTENSIONS_SET . has ( fileExt ) ;
857+ const MAX_READ_SIZE = isImageExt ? MAX_READ_SIZE_IMAGE : MAX_READ_SIZE_TEXT ;
848858 const fileStat = yield * fileSystem . stat ( target . absolutePath ) . pipe (
849859 Effect . mapError (
850860 ( cause ) =>
@@ -861,7 +871,7 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
861871 const sizeBytes = Number ( fileStat . size ) ;
862872 if ( sizeBytes > MAX_READ_SIZE ) {
863873 return yield * new RouteRequestError ( {
864- message : `File is too large to display (${ ( sizeBytes / 1024 / 1024 ) . toFixed ( 1 ) } MB). Maximum supported size is 1MB .` ,
874+ message : `File is too large to display (${ ( sizeBytes / 1024 / 1024 ) . toFixed ( 1 ) } MB). Maximum supported size is ${ ( MAX_READ_SIZE / 1024 / 1024 ) . toFixed ( 0 ) } MB .` ,
865875 } ) ;
866876 }
867877 // Read raw bytes to detect binary files
@@ -875,13 +885,48 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
875885 ) ;
876886 // Check for null bytes in the first 8KB to detect binary files
877887 const checkLength = Math . min ( rawBytes . length , 8192 ) ;
888+ let isBinary = false ;
878889 for ( let i = 0 ; i < checkLength ; i ++ ) {
879890 if ( rawBytes [ i ] === 0 ) {
880- return yield * new RouteRequestError ( {
881- message : `File appears to be binary and cannot be displayed: ${ target . relativePath } ` ,
882- } ) ;
891+ isBinary = true ;
892+ break ;
883893 }
884894 }
895+ if ( isBinary ) {
896+ // Check if the file is an image by extension
897+ const IMAGE_EXTENSIONS : Record < string , string > = {
898+ ".png" : "image/png" ,
899+ ".jpg" : "image/jpeg" ,
900+ ".jpeg" : "image/jpeg" ,
901+ ".gif" : "image/gif" ,
902+ ".bmp" : "image/bmp" ,
903+ ".webp" : "image/webp" ,
904+ ".ico" : "image/x-icon" ,
905+ ".tiff" : "image/tiff" ,
906+ ".tif" : "image/tiff" ,
907+ ".avif" : "image/avif" ,
908+ ".svg" : "image/svg+xml" ,
909+ } ;
910+ const ext = target . relativePath
911+ . substring ( target . relativePath . lastIndexOf ( "." ) )
912+ . toLowerCase ( ) ;
913+ const mimeType = IMAGE_EXTENSIONS [ ext ] ;
914+ if ( mimeType ) {
915+ // Return image as base64 data URL
916+ const base64 = Buffer . from ( rawBytes ) . toString ( "base64" ) ;
917+ const imageDataUrl = `data:${ mimeType } ;base64,${ base64 } ` ;
918+ return {
919+ relativePath : target . relativePath ,
920+ contents : "" ,
921+ sizeBytes,
922+ truncated : false ,
923+ imageDataUrl,
924+ } ;
925+ }
926+ return yield * new RouteRequestError ( {
927+ message : `File appears to be binary and cannot be displayed: ${ target . relativePath } ` ,
928+ } ) ;
929+ }
885930 const contents = new TextDecoder ( ) . decode ( rawBytes ) ;
886931 return {
887932 relativePath : target . relativePath ,
0 commit comments