1+ import { loadPackage } from "../../common/utils" ;
2+
13export function extract_page_content (
24 max_url_length = 200 ,
35 max_content_length = 50000
@@ -131,30 +133,102 @@ export function mark_screenshot_highlight_elements(
131133) : Promise < string > {
132134 return new Promise < string > ( async ( resolve , reject ) => {
133135 try {
134- // Convert base64 to Blob
135- const base64Data = screenshot . imageBase64 ;
136- const binaryString = atob ( base64Data ) ;
137- const bytes = new Uint8Array ( binaryString . length ) ;
138- for ( let i = 0 ; i < binaryString . length ; i ++ ) {
139- bytes [ i ] = binaryString . charCodeAt ( i ) ;
140- }
141- const blob = new Blob ( [ bytes ] , { type : screenshot . imageType } ) ;
142- const imageBitmap = await createImageBitmap ( blob , {
143- resizeQuality : "high" ,
144- resizeWidth : client_rect . width ,
145- resizeHeight : client_rect . height ,
146- } ) ;
147- const canvas = new OffscreenCanvas ( imageBitmap . width , imageBitmap . height ) ;
136+ const hasOffscreen = typeof OffscreenCanvas !== "undefined" ;
137+ const hasCreateImageBitmap = typeof createImageBitmap !== "undefined" ;
138+ const hasDOM = typeof document !== "undefined" && typeof Image !== "undefined" ;
139+ // @ts -ignore
140+ const isNode = typeof window === "undefined" && typeof process !== "undefined" && ! ! process . versions && ! ! process . versions . node ;
141+
142+ const loadImageAny = async ( ) => {
143+ if ( hasCreateImageBitmap ) {
144+ const base64Data = screenshot . imageBase64 ;
145+ const binaryString = atob ( base64Data ) ;
146+ const bytes = new Uint8Array ( binaryString . length ) ;
147+ for ( let i = 0 ; i < binaryString . length ; i ++ ) {
148+ bytes [ i ] = binaryString . charCodeAt ( i ) ;
149+ }
150+ const blob = new Blob ( [ bytes ] , { type : screenshot . imageType } ) ;
151+ const imageBitmap = await createImageBitmap ( blob , {
152+ resizeQuality : "high" ,
153+ resizeWidth : client_rect . width ,
154+ resizeHeight : client_rect . height ,
155+ } as any ) ;
156+ return { img : imageBitmap } ;
157+ }
158+ if ( hasDOM ) {
159+ const img = await new Promise < HTMLImageElement > (
160+ ( resolveImg , rejectImg ) => {
161+ const image = new Image ( ) ;
162+ image . onload = ( ) => resolveImg ( image ) ;
163+ image . onerror = ( e ) => rejectImg ( e ) ;
164+ image . src = `data:${ screenshot . imageType } ;base64,${ screenshot . imageBase64 } ` ;
165+ }
166+ ) ;
167+ return { img } ;
168+ }
169+ if ( isNode ) {
170+ const canvasMod = await loadPackage ( "canvas" ) ;
171+ const { loadImage } = canvasMod as any ;
172+ const dataUrl = `data:${ screenshot . imageType } ;base64,${ screenshot . imageBase64 } ` ;
173+ const img = await loadImage ( dataUrl ) ;
174+ return { img } ;
175+ }
176+ throw new Error ( "No image environment available" ) ;
177+ } ;
178+
179+ const createCanvasAny = async ( width : number , height : number ) => {
180+ if ( hasOffscreen ) {
181+ const canvas = new OffscreenCanvas ( width , height ) as any ;
182+ return {
183+ ctx : canvas . getContext ( "2d" ) as any ,
184+ exportDataUrl : async ( mime : string ) => {
185+ const blob = await canvas . convertToBlob ( { type : mime } ) ;
186+ return await new Promise < string > ( ( res , rej ) => {
187+ const reader = new FileReader ( ) ;
188+ reader . onloadend = ( ) => res ( reader . result as string ) ;
189+ reader . onerror = ( ) =>
190+ rej ( new Error ( "Failed to convert blob to base64" ) ) ;
191+ reader . readAsDataURL ( blob ) ;
192+ } ) ;
193+ } ,
194+ } ;
195+ }
196+ if ( hasDOM ) {
197+ const canvas = document . createElement ( "canvas" ) ;
198+ canvas . width = width ;
199+ canvas . height = height ;
200+ return {
201+ ctx : canvas . getContext ( "2d" ) as any ,
202+ exportDataUrl : async ( mime : string ) => canvas . toDataURL ( mime ) ,
203+ } ;
204+ }
205+ if ( isNode ) {
206+ const canvasMod = await loadPackage ( "canvas" ) ;
207+ const { createCanvas } = canvasMod as any ;
208+ const canvas = createCanvas ( width , height ) ;
209+ return {
210+ ctx : canvas . getContext ( "2d" ) ,
211+ exportDataUrl : async ( mime : string ) => canvas . toDataURL ( mime ) ,
212+ } ;
213+ }
214+ throw new Error ( "No canvas environment available" ) ;
215+ } ;
148216
149- const ctx = canvas . getContext ( "2d" ) ;
217+ const loaded = await loadImageAny ( ) ;
218+ const targetWidth = client_rect . width ;
219+ const targetHeight = client_rect . height ;
220+ const { ctx, exportDataUrl } = await createCanvasAny (
221+ targetWidth ,
222+ targetHeight
223+ ) ;
150224 if ( ! ctx ) {
151225 reject ( new Error ( "Failed to get canvas context" ) ) ;
152226 return ;
153227 }
154228
155229 ctx . imageSmoothingEnabled = true ;
156230 ctx . imageSmoothingQuality = "high" ;
157- ctx . drawImage ( imageBitmap , 0 , 0 ) ;
231+ ctx . drawImage ( loaded . img , 0 , 0 , targetWidth , targetHeight ) ;
158232
159233 const sortedEntries = Object . entries ( area_map )
160234 . filter ( ( [ id , area ] ) => area . width > 0 && area . height > 0 )
@@ -163,7 +237,7 @@ export function mark_screenshot_highlight_elements(
163237 const areaB = b [ 1 ] . width * b [ 1 ] . height ;
164238 return areaB - areaA ;
165239 } ) ;
166-
240+
167241 const colors = [
168242 "#FF0000" ,
169243 "#00FF00" ,
@@ -178,10 +252,8 @@ export function mark_screenshot_highlight_elements(
178252 "#DC143C" ,
179253 "#4682B4" ,
180254 ] ;
181-
182255 sortedEntries . forEach ( ( [ id , area ] , index ) => {
183256 const color = colors [ index % colors . length ] ;
184-
185257 if ( area . width * area . height < 40000 ) {
186258 // Draw a background color
187259 ctx . fillStyle = color + "1A" ;
@@ -196,9 +268,10 @@ export function mark_screenshot_highlight_elements(
196268 // Draw ID tag background
197269 const fontSize = Math . min ( 12 , Math . max ( 8 , area . height / 2 ) ) ;
198270 ctx . font = `${ fontSize } px sans-serif` ;
199- const textMetrics = ctx . measureText ( id ) ;
271+ const metrics : any = ctx . measureText ( id ) as any ;
272+ const textWidth = metrics && metrics . width ? metrics . width : 0 ;
200273 const padding = 4 ;
201- const labelWidth = textMetrics . width + padding * 2 ;
274+ const labelWidth = textWidth + padding * 2 ;
202275 const labelHeight = fontSize + padding * 2 ;
203276
204277 // The tag position is in the upper right corner.
@@ -221,20 +294,9 @@ export function mark_screenshot_highlight_elements(
221294 ctx . fillText ( id , labelX + padding , labelY + padding ) ;
222295 } ) ;
223296
224- // Convert OffscreenCanvas to Blob, then to base64
225- const resultBlob = await canvas . convertToBlob ( {
226- type : screenshot . imageType ,
227- } ) ;
228-
229- const reader = new FileReader ( ) ;
230- reader . onloadend = ( ) => {
231- const resultBase64 = reader . result as string ;
232- resolve ( resultBase64 ) ;
233- } ;
234- reader . onerror = ( ) => {
235- reject ( new Error ( "Failed to convert blob to base64" ) ) ;
236- } ;
237- reader . readAsDataURL ( resultBlob ) ;
297+ // Export the image
298+ const out = await exportDataUrl ( screenshot . imageType ) ;
299+ resolve ( out ) ;
238300 } catch ( error ) {
239301 reject ( error ) ;
240302 }
0 commit comments