@@ -5093,3 +5093,256 @@ try {
50935093 ( document . head || document . documentElement || document . body ) . appendChild ( s ) ;
50945094 } catch ( e ) { console . warn ( 'ensureInjectBmMask failed' , e ) ; }
50955095} ) ( ) ;
5096+
5097+ ( function WplaceFastPaint ( ) {
5098+ try {
5099+ if ( window . __wplace_fast_paint_installed ) return ;
5100+ window . __wplace_fast_paint_installed = true ;
5101+
5102+ const TARGET_SELECTOR = '.rounded-t-box.bg-base-100.border-base-300.w-full.border-t.py-3' ;
5103+ const STEP = 2 ; // 每两个像素点击一次
5104+ const BATCH_RESOLVE = 16384 ; // 预解析 elementFromPoint 每批大小
5105+ const YIELD_EVERY = 65536 ; // 每多少次点击做一次最小 yield
5106+ const POST_MSG = '__WPLACE_FAST_PAINT_YIELD__' + Math . random ( ) ;
5107+
5108+ // minimal yield via postMessage (very cheap)
5109+ let yieldResolve = null ;
5110+ window . addEventListener ( 'message' , ( e ) => {
5111+ if ( e && e . data === POST_MSG && typeof yieldResolve === 'function' ) {
5112+ const r = yieldResolve ; yieldResolve = null ; r ( ) ;
5113+ }
5114+ } , false ) ;
5115+ const minimalYield = ( ) => new Promise ( res => { yieldResolve = res ; window . postMessage ( POST_MSG , '*' ) ; } ) ;
5116+
5117+ // 状态
5118+ let waitingForClicks = false ;
5119+ let startPt = null ;
5120+ let endPt = null ;
5121+ let abortFlag = false ;
5122+ let overlay = null ;
5123+
5124+ // 检查页面是否含目标元素
5125+ function pageHasTarget ( ) {
5126+ try { return ! ! document . querySelector ( TARGET_SELECTOR ) ; } catch ( e ) { return false ; }
5127+ }
5128+
5129+ // overlay 用于绘制矩形(若需要)但不用于消息,消息改为使用 showToast
5130+ function makeOverlay ( ) {
5131+ if ( overlay && document . body . contains ( overlay ) ) return overlay ;
5132+ const o = document . createElement ( 'div' ) ;
5133+ o . id = '__wplace_fast_paint_overlay' ;
5134+ Object . assign ( o . style , {
5135+ position : 'fixed' , left : '0' , top : '0' , width : '100vw' , height : '100vh' ,
5136+ zIndex : 2147483646 , pointerEvents : 'none' , fontFamily : 'sans-serif'
5137+ } ) ;
5138+ document . body . appendChild ( o ) ;
5139+ overlay = o ;
5140+ return o ;
5141+ }
5142+ function clearOverlay ( ) {
5143+ try { if ( overlay ) overlay . innerHTML = '' ; } catch ( e ) { }
5144+ }
5145+ function toast ( msg , timeout = 2000 ) {
5146+ try {
5147+ if ( typeof showToast === 'function' ) {
5148+ try { showToast ( msg , timeout ) ; return ; } catch ( e ) { }
5149+ }
5150+ // fallback to console and simple inline toast if showToast missing
5151+ console . log ( '[fast-paint] ' + msg ) ;
5152+ try {
5153+ const id = '__wplace_fast_paint_fallback_toast' ;
5154+ let el = document . getElementById ( id ) ;
5155+ if ( ! el ) {
5156+ el = document . createElement ( 'div' ) ;
5157+ el . id = id ;
5158+ Object . assign ( el . style , {
5159+ position : 'fixed' , right : '12px' , bottom : '12px' , zIndex : 2147483647 ,
5160+ background : 'rgba(0,0,0,0.72)' , color : '#e8faff' , padding : '8px 10px' ,
5161+ borderRadius : '6px' , fontSize : '13px' , pointerEvents : 'none' , transition : 'opacity 0.15s linear'
5162+ } ) ;
5163+ document . documentElement . appendChild ( el ) ;
5164+ }
5165+ el . textContent = msg ;
5166+ el . style . opacity = '1' ;
5167+ setTimeout ( ( ) => { try { el . style . opacity = '0' ; } catch ( _ ) { } } , timeout ) ;
5168+ } catch ( e ) { }
5169+ } catch ( e ) { }
5170+ }
5171+
5172+ function drawRect ( left , top , w , h ) {
5173+ try {
5174+ const o = makeOverlay ( ) ;
5175+ o . innerHTML = '' ;
5176+ const rect = document . createElement ( 'div' ) ;
5177+ Object . assign ( rect . style , {
5178+ position : 'absolute' , left : left + 'px' , top : top + 'px' ,
5179+ width : Math . max ( 0 , w ) + 'px' , height : Math . max ( 0 , h ) + 'px' ,
5180+ border : '2px dashed rgba(0,200,255,0.9)' , background : 'rgba(0,200,255,0.06)' ,
5181+ boxSizing : 'border-box' , pointerEvents : 'none' , zIndex : 2147483647
5182+ } ) ;
5183+ o . appendChild ( rect ) ;
5184+ } catch ( e ) { }
5185+ }
5186+
5187+ // Synth real-like click: pointerdown -> pointerup -> click + mouse events as fallback
5188+ function dispatchClickAt ( x , y ) {
5189+ try {
5190+ const target = document . elementFromPoint ( x , y ) || document . body ;
5191+ const opts = { bubbles : true , cancelable : true , composed : true , clientX : x , clientY : y , screenX : window . screenX + x , screenY : window . screenY + y , button : 0 , buttons : 1 } ;
5192+ try { target . dispatchEvent ( new PointerEvent ( 'pointerdown' , Object . assign ( { pointerId : 1 , isPrimary : true , pointerType : 'mouse' } , opts ) ) ) ; } catch ( e ) { }
5193+ try { target . dispatchEvent ( new MouseEvent ( 'mousedown' , opts ) ) ; } catch ( e ) { }
5194+ try { target . dispatchEvent ( new PointerEvent ( 'pointerup' , Object . assign ( { pointerId : 1 , isPrimary : true , pointerType : 'mouse' } , opts ) ) ) ; } catch ( e ) { }
5195+ try { target . dispatchEvent ( new MouseEvent ( 'mouseup' , opts ) ) ; } catch ( e ) { }
5196+ try { target . dispatchEvent ( new MouseEvent ( 'click' , opts ) ) ; } catch ( e ) { }
5197+ } catch ( e ) { }
5198+ }
5199+
5200+ // 预计算 targets 并执行点击(行主序)
5201+ async function runFastClicks ( tl , br , step = STEP ) {
5202+ abortFlag = false ;
5203+ // 规范化边界(整数)
5204+ const left = Math . max ( 0 , Math . min ( tl . x , br . x ) ) | 0 ;
5205+ const right = Math . max ( 0 , Math . max ( tl . x , br . x ) ) | 0 ;
5206+ const top = Math . max ( 0 , Math . min ( tl . y , br . y ) ) | 0 ;
5207+ const bottom = Math . max ( 0 , Math . max ( tl . y , br . y ) ) | 0 ;
5208+
5209+ // 预构造所有点(仅坐标)
5210+ const coords = [ ] ;
5211+ for ( let y = top ; y <= bottom ; y += step ) {
5212+ for ( let x = left ; x <= right ; x += step ) {
5213+ coords . push ( { x : x | 0 , y : y | 0 } ) ;
5214+ }
5215+ }
5216+
5217+ // 预解析 elementFromPoint 分批(避免一次阻塞过久)
5218+ for ( let s = 0 ; s < coords . length ; s += BATCH_RESOLVE ) {
5219+ const e = Math . min ( s + BATCH_RESOLVE , coords . length ) ;
5220+ for ( let k = s ; k < e ; k ++ ) {
5221+ const c = coords [ k ] ;
5222+ c . el = document . elementFromPoint ( c . x , c . y ) || document . body ;
5223+ }
5224+ if ( e < coords . length ) await minimalYield ( ) ;
5225+ }
5226+
5227+ // 现已将元素解析到 coords[].el,开始快速 dispatch
5228+ const total = coords . length ;
5229+ let idx = 0 ;
5230+ toast ( `Fast paint started: 0% (0/${ total } )` , 3000 ) ;
5231+ while ( idx < total ) {
5232+ if ( abortFlag ) {
5233+ toast ( 'Fast paint cancelled' , 2000 ) ;
5234+ clearOverlay ( ) ;
5235+ return { cancelled : true } ;
5236+ }
5237+ // 每次处理一小批以保持浏览器响应
5238+ const batch = Math . min ( 8192 , total - idx ) ;
5239+ for ( let i = 0 ; i < batch ; i ++ ) {
5240+ const c = coords [ idx + i ] ;
5241+ const targetEl = ( c . el && document . body . contains ( c . el ) ) ? c . el : ( document . elementFromPoint ( c . x , c . y ) || document . body ) ;
5242+ try {
5243+ const opts = { bubbles : true , cancelable : true , composed : true , clientX : c . x , clientY : c . y , screenX : window . screenX + c . x , screenY : window . screenY + c . y , button : 0 , buttons : 1 } ;
5244+ try { targetEl . dispatchEvent ( new PointerEvent ( 'pointerdown' , Object . assign ( { pointerId : 1 , isPrimary : true , pointerType : 'mouse' } , opts ) ) ) ; } catch ( e ) { }
5245+ try { targetEl . dispatchEvent ( new MouseEvent ( 'mousedown' , opts ) ) ; } catch ( e ) { }
5246+ try { targetEl . dispatchEvent ( new PointerEvent ( 'pointerup' , Object . assign ( { pointerId : 1 , isPrimary : true , pointerType : 'mouse' } , opts ) ) ) ; } catch ( e ) { }
5247+ try { targetEl . dispatchEvent ( new MouseEvent ( 'mouseup' , opts ) ) ; } catch ( e ) { }
5248+ try { targetEl . dispatchEvent ( new MouseEvent ( 'click' , opts ) ) ; } catch ( e ) { }
5249+ } catch ( e ) { }
5250+ }
5251+ idx += batch ;
5252+ const pct = Math . round ( ( idx / total ) * 100 ) ;
5253+ toast ( `Fast paint: ${ pct } % (${ idx } /${ total } )` , 1500 ) ;
5254+ if ( idx % YIELD_EVERY === 0 ) await minimalYield ( ) ;
5255+ await minimalYield ( ) ;
5256+ }
5257+
5258+ toast ( 'Fast paint finished' , 2000 ) ;
5259+ setTimeout ( ( ) => { try { clearOverlay ( ) ; } catch ( e ) { } } , 800 ) ;
5260+ return { cancelled : false } ;
5261+ }
5262+
5263+ // capture two clicks (left-top, right-bottom)
5264+ function onCaptureClick ( ev ) {
5265+ try {
5266+ // ignore clicks inside our UI panel
5267+ const path = ev . composedPath ? ev . composedPath ( ) : ( ev . path || [ ] ) ;
5268+ for ( const n of path ) {
5269+ try { if ( n && n . id === 'wplace_versatile_tool' ) return ; } catch ( e ) { }
5270+ }
5271+ } catch ( e ) { }
5272+ ev . preventDefault ( ) ; ev . stopPropagation ( ) ;
5273+
5274+ const pt = { x : ev . clientX , y : ev . clientY } ;
5275+ if ( ! startPt ) {
5276+ startPt = pt ;
5277+ drawRect ( startPt . x , startPt . y , 1 , 1 ) ;
5278+ toast ( 'Fast paint: start saved, click bottom-right' , 1500 ) ;
5279+ return ;
5280+ }
5281+ if ( ! endPt ) {
5282+ endPt = pt ;
5283+ const left = Math . min ( startPt . x , endPt . x ) ;
5284+ const top = Math . min ( startPt . y , endPt . y ) ;
5285+ const w = Math . abs ( endPt . x - startPt . x ) ;
5286+ const h = Math . abs ( endPt . y - startPt . y ) ;
5287+ drawRect ( left , top , w , h ) ;
5288+ try { document . removeEventListener ( 'pointerdown' , onCaptureClick , true ) ; } catch ( e ) { }
5289+ waitingForClicks = false ;
5290+ runFastClicks ( startPt , endPt , STEP ) . catch ( err => { console . error ( 'fast paint error' , err ) ; toast ( 'Fast paint error' , 2000 ) ; setTimeout ( clearOverlay , 800 ) ; } ) ;
5291+ }
5292+ }
5293+
5294+ function cancelCapture ( ) {
5295+ abortFlag = true ;
5296+ waitingForClicks = false ;
5297+ startPt = null ;
5298+ endPt = null ;
5299+ try { document . removeEventListener ( 'pointerdown' , onCaptureClick , true ) ; } catch ( e ) { }
5300+ toast ( 'Fast paint cancelled' , 1500 ) ;
5301+ setTimeout ( clearOverlay , 600 ) ;
5302+ }
5303+
5304+ // key handler: only activate when page has target element
5305+ function onKeyDown ( e ) {
5306+ try {
5307+ if ( e . repeat ) return ;
5308+ // ignore typing
5309+ const active = document . activeElement ;
5310+ const tag = ( active && active . tagName || '' ) . toLowerCase ( ) ;
5311+ if ( active && ( active . isContentEditable || tag === 'input' || tag === 'textarea' || tag === 'select' ) ) return ;
5312+
5313+ if ( ( e . key === 'z' || e . key === 'Z' ) && pageHasTarget ( ) ) {
5314+ if ( waitingForClicks ) return ;
5315+ waitingForClicks = true ;
5316+ startPt = null ; endPt = null ; abortFlag = false ;
5317+ toast ( 'Fast paint: click top-left then bottom-right (Esc to cancel)' , 2000 ) ;
5318+ try { document . addEventListener ( 'pointerdown' , onCaptureClick , true ) ; } catch ( err ) { }
5319+ makeOverlay ( ) ;
5320+ } else if ( e . key === 'Escape' ) {
5321+ if ( waitingForClicks || startPt || endPt ) cancelCapture ( ) ;
5322+ }
5323+ } catch ( err ) { }
5324+ }
5325+
5326+ window . addEventListener ( 'keydown' , onKeyDown , true ) ;
5327+
5328+ // expose simple API
5329+ window . __wplace_fast_paint_control = {
5330+ isInstalled : true ,
5331+ status : ( ) => ( { waitingForClicks, startPt, endPt, abortFlag, hasTarget : pageHasTarget ( ) } ) ,
5332+ cancel : cancelCapture ,
5333+ uninstall : function ( ) {
5334+ try {
5335+ window . removeEventListener ( 'keydown' , onKeyDown , true ) ;
5336+ document . removeEventListener ( 'pointerdown' , onCaptureClick , true ) ;
5337+ } catch ( e ) { }
5338+ try { clearOverlay ( ) ; } catch ( e ) { }
5339+ delete window . __wplace_fast_paint_installed ;
5340+ delete window . __wplace_fast_paint_control ;
5341+ }
5342+ } ;
5343+
5344+ console . log ( 'Wplace fast paint installed (showToast). Press Z (when target exists), click top-left then bottom-right. Esc to cancel.' ) ;
5345+ } catch ( e ) {
5346+ console . warn ( 'installWplaceFastPaintUsingShowToast failed' , e ) ;
5347+ }
5348+ } ) ( ) ;
0 commit comments