@@ -74,22 +74,45 @@ export async function executeBrowserAnnotation(page: Page, step: BrowserInteract
7474 const padding = Number ( spec . style ?. padding ?? 0 )
7575 Object . assign ( node . style , { left : `${ value . left - padding } px` , top : `${ value . top - padding } px` , width : `${ value . width + padding * 2 } px` , height : `${ value . height + padding * 2 } px` } )
7676 }
77- function render ( ) {
78- const value = rect ( )
79- if ( ! value ) return
80- if ( spec . shape === "highlight" ) setRect ( value )
81- if ( spec . shape === "label" ) Object . assign ( node . style , { left : `${ value . left } px` , top : `${ spec . anchor ?. placement === "bottom" ? value . bottom + 8 : value . top - 44 } px` } )
82- if ( spec . shape === "spotlight" ) { const p = Number ( spec . style ?. padding ?? 8 ) ; const l = value . left - p , t = value . top - p , w = value . width + p * 2 , h = value . height + p * 2 ; Object . assign ( node . style , { clipPath :`polygon(0 0,100% 0,100% 100%,0 100%,0 0,${ l } px ${ t } px,${ l } px ${ t + h } px,${ l + w } px ${ t + h } px,${ l + w } px ${ t } px,${ l } px ${ t } px)` , background :"rgba(0,0,0,.62)" } ) }
83- if ( spec . shape === "arrow" ) {
84- const direction = spec . direction || "bottom-left"
85- const [ vertical , horizontal ] = direction . split ( "-" ) . length === 2 ? direction . split ( "-" ) : [ direction , "center" ]
86- Object . assign ( node . style , { left : `${ horizontal === "left" ? value . left - 50 : horizontal === "right" ? value . right + 10 : value . left + value . width / 2 } px` , top : `${ vertical === "top" ? value . top - 50 : vertical === "bottom" ? value . bottom + 10 : value . top + value . height / 2 } px` } )
87- }
77+ function render ( ) {
78+ const value = rect ( )
79+ if ( ! value ) return
80+ if ( spec . shape === "highlight" ) setRect ( value )
81+ if ( spec . shape === "label" ) {
82+ const gap = 12
83+ const width = node . offsetWidth
84+ const height = node . offsetHeight
85+ const placement = spec . anchor ?. placement === "bottom" ? "bottom" : "top"
86+ const canPlace = ( side : string ) => side === "top" ? value . top >= height + gap : innerHeight - value . bottom >= height + gap
87+ const opposite = placement === "top" ? "bottom" : "top"
88+ const side = canPlace ( placement ) ? placement : canPlace ( opposite ) ? opposite : placement
89+ const left = Math . max ( 8 , Math . min ( value . left , innerWidth - width - 8 ) )
90+ const top = Math . max ( 8 , Math . min ( side === "bottom" ? value . bottom + gap : value . top - height - gap , innerHeight - height - 8 ) )
91+ Object . assign ( node . style , { left : `${ left } px` , top : `${ top } px` } )
92+ }
93+ if ( spec . shape === "spotlight" ) { const p = Number ( spec . style ?. padding ?? 8 ) ; const l = value . left - p , t = value . top - p , w = value . width + p * 2 , h = value . height + p * 2 ; Object . assign ( node . style , { clipPath :`polygon(0 0,100% 0,100% 100%,0 100%,0 0,${ l } px ${ t } px,${ l } px ${ t + h } px,${ l + w } px ${ t + h } px,${ l + w } px ${ t } px,${ l } px ${ t } px)` , background :"rgba(0,0,0,.62)" } ) }
94+ if ( spec . shape === "arrow" ) {
95+ const direction = spec . direction || "bottom-left"
96+ const [ dx , dy ] = direction === "left" ? [ - 1 , 0 ] : direction === "right" ? [ 1 , 0 ] : direction === "top" ? [ 0 , - 1 ] : direction === "bottom" ? [ 0 , 1 ] : direction === "top-left" ? [ - 1 , - 1 ] : direction === "top-right" ? [ 1 , - 1 ] : direction === "bottom-right" ? [ 1 , 1 ] : [ - 1 , 1 ]
97+ const length = 56
98+ const ux = dx / Math . hypot ( dx , dy ) , uy = dy / Math . hypot ( dx , dy )
99+ const endX = value . left + value . width / 2 - ux * ( dx && dy ? value . width / 2 / Math . SQRT2 : dx ? value . width / 2 : 0 )
100+ const endY = value . top + value . height / 2 - uy * ( dx && dy ? value . height / 2 / Math . SQRT2 : dy ? value . height / 2 : 0 )
101+ const startX = endX + ux * length , startY = endY + uy * length
102+ const bounds = { left : Math . min ( startX , endX ) - 10 , top : Math . min ( startY , endY ) - 10 }
103+ node . style . left = `${ bounds . left } px` ; node . style . top = `${ bounds . top } px`
104+ const svg = node . querySelector ( "svg" ) !
105+ const shaft = svg . querySelector ( "path" ) !
106+ const sx = startX - bounds . left , sy = startY - bounds . top , ex = endX - bounds . left , ey = endY - bounds . top
107+ const head = 10 , angle = Math . atan2 ( ey - sy , ex - sx ) , spread = Math . PI / 6
108+ shaft . setAttribute ( "d" , `M ${ sx } ${ sy } L ${ ex } ${ ey } M ${ ex - head * Math . cos ( angle - spread ) } ${ ey - head * Math . sin ( angle - spread ) } L ${ ex } ${ ey } L ${ ex - head * Math . cos ( angle + spread ) } ${ ey - head * Math . sin ( angle + spread ) } ` )
109+ svg . setAttribute ( "width" , "80" ) ; svg . setAttribute ( "height" , "80" ) ; svg . setAttribute ( "viewBox" , "0 0 80 80" )
110+ }
88111 }
89112 Object . assign ( node . style , { position : "fixed" , boxSizing : "border-box" , pointerEvents : "none" , transition : spec . animate === "fade" || spec . animate === undefined ? "opacity .25s ease" : "none" , opacity : "1" } )
90113 if ( spec . shape === "highlight" ) Object . assign ( node . style , { border : `${ Number ( theme . strokeWidth ?? 3 ) } px solid ${ color } ` , borderRadius : spec . style ?. variant === "box" ? `${ Number ( theme . radius ?? 0 ) } px` : "999px" } )
91114 if ( spec . shape === "spotlight" ) Object . assign ( node . style , { inset : "0" } )
92- if ( spec . shape === "arrow" ) { const svg = document . createElementNS ( "http://www.w3.org/2000/svg" , "svg" ) ; svg . setAttribute ( "width" , "88 " ) ; svg . setAttribute ( "height" , "64 " ) ; svg . setAttribute ( "viewBox" , "0 0 88 64 " ) ; const path = document . createElementNS ( svg . namespaceURI , "path" ) ; path . setAttribute ( "d" , "M4 56 Q30 52 72 12 M54 12 L72 12 L72 30" ) ; path . setAttribute ( " fill", "none" ) ; path . setAttribute ( "stroke" , color ) ; path . setAttribute ( "stroke-width" , String ( theme . strokeWidth ?? 3 ) ) ; path . setAttribute ( "stroke-linecap" , "round" ) ; path . setAttribute ( "stroke-linejoin" , "round" ) ; if ( spec . animate === "draw" ) { path . setAttribute ( "stroke-dasharray" , "120" ) ; path . setAttribute ( "stroke-dashoffset" , "120" ) ; path . animate ( [ { strokeDashoffset :"120" } , { strokeDashoffset :"0" } ] , { duration :600 , fill :"forwards" } ) } svg . append ( path ) ; node . append ( svg ) ; Object . assign ( node . style , { transform : spec . direction ?. startsWith ( "top" ) ? "rotate(180deg)" : spec . direction ?. startsWith ( "left" ) ? "rotate(90deg)" : spec . direction ?. startsWith ( "right" ) ? "rotate(-90deg)" : "none" } ) }
115+ if ( spec . shape === "arrow" ) { const svg = document . createElementNS ( "http://www.w3.org/2000/svg" , "svg" ) ; svg . setAttribute ( "width" , "80 " ) ; svg . setAttribute ( "height" , "80 " ) ; svg . setAttribute ( "viewBox" , "0 0 80 80 " ) ; const path = document . createElementNS ( svg . namespaceURI , "path" ) ; path . setAttribute ( "fill" , "none" ) ; path . setAttribute ( "stroke" , color ) ; path . setAttribute ( "stroke-width" , String ( theme . strokeWidth ?? 3 ) ) ; path . setAttribute ( "stroke-linecap" , "round" ) ; path . setAttribute ( "stroke-linejoin" , "round" ) ; if ( spec . animate === "draw" ) { path . setAttribute ( "stroke-dasharray" , "120" ) ; path . setAttribute ( "stroke-dashoffset" , "120" ) ; path . animate ( [ { strokeDashoffset :"120" } , { strokeDashoffset :"0" } ] , { duration :600 , fill :"forwards" } ) } svg . append ( path ) ; node . append ( svg ) }
93116 if ( spec . shape === "label" || spec . shape === "caption" ) {
94117 node . textContent = spec . text || ""
95118 Object . assign ( node . style , { padding : "8px 12px" , borderRadius : `${ Number ( theme . radius ?? 6 ) } px` , color : textColor , background, fontFamily : theme . fontFamily ?? "system-ui, sans-serif" , fontSize : `${ Number ( theme . fontSize ?? 16 ) } px` , whiteSpace : "nowrap" } )
0 commit comments