@@ -4247,7 +4247,7 @@ try { installShareAndFavHandlers(); } catch (e) { console.warn('installShareAndF
42474247
42484248 /* Right-button long-press acts like pressing Q (with smoother tuning) */
42494249 ( function installRightHoldAsQ ( ) {
4250- const HOLD_MS = Number ( state . cfg . rightHoldMs || 500 ) ;
4250+ const HOLD_MS = Number ( state . cfg . rightHoldMs ) ;
42514251 const MOVE_THRESHOLD = Number ( state . cfg . rightHoldMoveThreshold || 10 ) ;
42524252 let rightState = { pressed : false , timer : null , active : false , startX : 0 , startY : 0 , moved : false , suppressHandler : null } ;
42534253
@@ -4435,3 +4435,240 @@ try {
44354435 }
44364436} catch ( e ) { }
44374437} ) ( ) ;
4438+ // 隐藏blue marble或是skirk marble
4439+ ( function installZToggleForBmDashDash ( ) {
4440+ try {
4441+ if ( window . __wplace_z_bm_dashdash_installed ) return ;
4442+ window . __wplace_z_bm_dashdash_installed = true ;
4443+
4444+ let state = {
4445+ isHidden : false ,
4446+ originalNode : null ,
4447+ parent : null ,
4448+ nextSibling : null ,
4449+ oldInlineStyle : null ,
4450+ oldAriaHidden : null ,
4451+ dataMarked : false ,
4452+ cloneFallback : null
4453+ } ;
4454+
4455+ function findBmCandidate ( ) {
4456+ try {
4457+ // 1) 精确优先匹配你提到的 id(bm-- -> bm-A -> bm-z)
4458+ const preferIds = [ 'bm--' , 'bm-A' , 'bm-A' . toLowerCase ( ) ] ; // bm-A is case-sensitive in DOM; we will try exact and then fallbacks
4459+ for ( const id of [ 'bm--' , 'bm-A' , 'bm-a' ] ) {
4460+ try {
4461+ const el = document . getElementById ( id ) ;
4462+ if ( el ) return el ;
4463+ } catch ( e ) { }
4464+ }
4465+
4466+ // 2) 如果存在 bm-z(内部控件),向上寻找最近包含 bm- 前缀的祖先容器
4467+ const elBmZ = document . getElementById ( 'bm-z' ) ;
4468+ if ( elBmZ ) {
4469+ let node = elBmZ ;
4470+ while ( node && node !== document . body ) {
4471+ if ( node . id && / ^ b m [ - _ ] / i. test ( node . id ) ) return node ;
4472+ if ( node . className && typeof node . className === 'string' && / ( ^ | \s ) b m ( - | _ ) ? / i. test ( node . className ) ) return node ;
4473+ node = node . parentElement ;
4474+ }
4475+ return elBmZ ;
4476+ }
4477+ } catch ( e ) { }
4478+
4479+ try {
4480+ // 3) 回退:寻找 id 以 bm- 开头且 DOM 结构看起来像面板(含输入、按钮或图片)
4481+ const bmPref = Array . from ( document . querySelectorAll ( '[id]' ) ) . find ( el => {
4482+ try {
4483+ if ( ! el . id ) return false ;
4484+ if ( ! / ^ b m [ - _ ] / i. test ( el . id ) ) return false ;
4485+ // 简单检查:包含输入、按钮或图片之一
4486+ return ! ! ( el . querySelector && ( el . querySelector ( 'input, button, img, textarea' ) || el . querySelector ( '[id^="bm-"]' ) ) ) ;
4487+ } catch ( e ) { return false ; }
4488+ } ) ;
4489+ if ( bmPref ) return bmPref ;
4490+ } catch ( e ) { }
4491+
4492+ try {
4493+ // 4) 最后回退:查找包含明显文本或图片特征的容器
4494+ const nodes = Array . from ( document . querySelectorAll ( 'div' ) ) . slice ( 0 , 800 ) ;
4495+ for ( const n of nodes ) {
4496+ try {
4497+ const text = ( n . innerText || '' ) . toLowerCase ( ) ;
4498+ if ( text . includes ( 'skirk marble' ) || text . includes ( 'blue marble' ) ) return n ;
4499+ if ( n . querySelector && n . querySelector ( 'img[alt*="Blue Marble"], img[alt*="Skirk"], img[alt*="Blue Marble Icon"]' ) ) return n ;
4500+ } catch ( e ) { }
4501+ }
4502+ } catch ( e ) { }
4503+
4504+ return null ;
4505+ }
4506+
4507+ function preserve ( node ) {
4508+ try {
4509+ state . originalNode = node ;
4510+ state . parent = node . parentElement || null ;
4511+ state . nextSibling = node . nextSibling || null ;
4512+ state . oldInlineStyle = node . getAttribute && node . getAttribute ( 'style' ) ;
4513+ state . oldAriaHidden = node . getAttribute && node . getAttribute ( 'aria-hidden' ) ;
4514+ state . dataMarked = ! ! ( node . getAttribute && node . getAttribute ( 'data-wplace-hidden-bm' ) ) ;
4515+ try { state . cloneFallback = node . cloneNode ( true ) ; } catch ( e ) { state . cloneFallback = null ; }
4516+ } catch ( e ) {
4517+ state . originalNode = node ;
4518+ }
4519+ }
4520+
4521+ function clearState ( ) {
4522+ state . originalNode = null ;
4523+ state . parent = null ;
4524+ state . nextSibling = null ;
4525+ state . oldInlineStyle = null ;
4526+ state . oldAriaHidden = null ;
4527+ state . dataMarked = false ;
4528+ state . cloneFallback = null ;
4529+ state . isHidden = false ;
4530+ }
4531+
4532+ function doHide ( node ) {
4533+ if ( ! node ) return false ;
4534+ preserve ( node ) ;
4535+ try { node . setAttribute && node . setAttribute ( 'data-wplace-hidden-bm' , '1' ) ; } catch ( e ) { }
4536+ try { node . setAttribute && node . setAttribute ( 'aria-hidden' , 'true' ) ; } catch ( e ) { }
4537+ try { node . setAttribute && node . setAttribute ( 'data-wplace-old-style' , node . getAttribute ( 'style' ) || '' ) ; } catch ( e ) { }
4538+
4539+ try {
4540+ // 使 top/right 生效:若 computed position 为 static 则设置 position:fixed
4541+ const comp = window . getComputedStyle ( node ) ;
4542+ if ( ! node . style . position && ( ! comp || comp . position === 'static' || comp . position === '' ) ) {
4543+ node . style . position = 'fixed' ;
4544+ }
4545+ // 设置目标坐标并彻底隐藏
4546+ node . style . top = '-100px' ;
4547+ node . style . right = '75px' ;
4548+ node . style . transition = 'opacity 120ms linear' ;
4549+ node . style . opacity = '0' ;
4550+ node . style . pointerEvents = 'none' ;
4551+ node . style . display = 'none' ;
4552+ } catch ( e ) { }
4553+
4554+ state . isHidden = true ;
4555+ return true ;
4556+ }
4557+
4558+ function insertNodeAtParent ( nodeToInsert ) {
4559+ try {
4560+ if ( ! state . parent ) {
4561+ document . body . appendChild ( nodeToInsert ) ;
4562+ return true ;
4563+ }
4564+ if ( state . nextSibling && state . parent . contains ( state . nextSibling ) ) {
4565+ state . parent . insertBefore ( nodeToInsert , state . nextSibling ) ;
4566+ } else {
4567+ state . parent . appendChild ( nodeToInsert ) ;
4568+ }
4569+ return true ;
4570+ } catch ( e ) {
4571+ try { state . parent . appendChild ( nodeToInsert ) ; return true ; } catch ( e2 ) { return false ; }
4572+ }
4573+ }
4574+
4575+ function doRestore ( ) {
4576+ try {
4577+ // 优先恢复原始节点(如果仍在 DOM 中)
4578+ if ( state . originalNode && document . body . contains ( state . originalNode ) ) {
4579+ const n = state . originalNode ;
4580+ const oldStyle = n . getAttribute && n . getAttribute ( 'data-wplace-old-style' ) ;
4581+ if ( typeof oldStyle !== 'undefined' && oldStyle !== null ) {
4582+ if ( oldStyle . length ) n . setAttribute ( 'style' , oldStyle ) ;
4583+ else n . removeAttribute && n . removeAttribute ( 'style' ) ;
4584+ try { n . removeAttribute ( 'data-wplace-old-style' ) ; } catch ( e ) { }
4585+ } else if ( state . oldInlineStyle ) {
4586+ n . setAttribute ( 'style' , state . oldInlineStyle || '' ) ;
4587+ } else {
4588+ n . removeAttribute && n . removeAttribute ( 'style' ) ;
4589+ }
4590+ if ( typeof state . oldAriaHidden === 'undefined' || state . oldAriaHidden === null ) n . removeAttribute && n . removeAttribute ( 'aria-hidden' ) ;
4591+ else n . setAttribute && n . setAttribute ( 'aria-hidden' , state . oldAriaHidden ) ;
4592+ if ( ! state . dataMarked ) n . removeAttribute && n . removeAttribute ( 'data-wplace-hidden-bm' ) ;
4593+ try { n . style . display = '' ; n . style . opacity = '' ; n . style . pointerEvents = '' ; } catch ( e ) { }
4594+ clearState ( ) ;
4595+ showToast && showToast ( '已恢复 bm 面板' ) ;
4596+ return true ;
4597+ }
4598+
4599+ // 原始节点不在 DOM,尝试用 cloneFallback 恢复
4600+ if ( state . cloneFallback ) {
4601+ let newNode ;
4602+ try { newNode = state . cloneFallback . cloneNode ( true ) ; } catch ( e ) { try { newNode = state . cloneFallback ; } catch ( e2 ) { newNode = null ; } }
4603+ if ( newNode ) {
4604+ const ok = insertNodeAtParent ( newNode ) ;
4605+ if ( ok ) {
4606+ newNode . removeAttribute && newNode . removeAttribute ( 'data-wplace-hidden-bm' ) ;
4607+ newNode . removeAttribute && newNode . removeAttribute ( 'aria-hidden' ) ;
4608+ try { newNode . style . display = '' ; newNode . style . opacity = '' ; newNode . style . pointerEvents = '' ; } catch ( e ) { }
4609+ state . originalNode = newNode ;
4610+ clearState ( ) ;
4611+ showToast && showToast ( '已用备份恢复(原始元素被移除)' ) ;
4612+ return true ;
4613+ }
4614+ }
4615+ }
4616+
4617+ clearState ( ) ;
4618+ showToast && showToast ( '无法恢复 bm 面板(原始节点丢失)' ) ;
4619+ return false ;
4620+ } catch ( e ) {
4621+ clearState ( ) ;
4622+ showToast && showToast ( '恢复失败' ) ;
4623+ return false ;
4624+ }
4625+ }
4626+
4627+ function toggle ( ) {
4628+ try {
4629+ if ( state . isHidden ) {
4630+ doRestore ( ) ;
4631+ return ;
4632+ }
4633+ const cand = findBmCandidate ( ) ;
4634+ if ( ! cand ) {
4635+ showToast && showToast ( '未找到 bm 面板' ) ;
4636+ return ;
4637+ }
4638+ doHide ( cand ) ;
4639+ showToast && showToast ( '已隐藏 bm 面板,按 Z 可复原' ) ;
4640+ } catch ( e ) { }
4641+ }
4642+
4643+ function onKeyDown ( e ) {
4644+ try {
4645+ if ( e && e . repeat ) return ;
4646+ const active = document . activeElement ;
4647+ const tag = ( active && active . tagName || '' ) . toLowerCase ( ) ;
4648+ if ( active && ( active . isContentEditable || tag === 'input' || tag === 'textarea' || tag === 'select' ) ) return ;
4649+ if ( e . key === 'z' || e . key === 'Z' ) {
4650+ e . preventDefault && e . preventDefault ( ) ;
4651+ e . stopPropagation && e . stopPropagation ( ) ;
4652+ toggle ( ) ;
4653+ }
4654+ } catch ( e ) { }
4655+ }
4656+
4657+ window . addEventListener ( 'keydown' , onKeyDown , true ) ;
4658+
4659+ window . __wplace_z_bm_dashdash = {
4660+ toggle : toggle ,
4661+ hide : function ( ) { const c = findBmCandidate ( ) ; return c ? doHide ( c ) : false ; } ,
4662+ restore : doRestore ,
4663+ find : findBmCandidate ,
4664+ status : function ( ) { return { isHidden : ! ! state . isHidden , hasOriginal : ! ! state . originalNode , parent : state . parent || null } ; } ,
4665+ uninstall : function ( ) {
4666+ try { window . removeEventListener ( 'keydown' , onKeyDown , true ) ; } catch ( e ) { }
4667+ window . __wplace_z_bm_dashdash_installed = false ;
4668+ }
4669+ } ;
4670+
4671+ } catch ( e ) {
4672+ console . warn ( 'installZToggleForBmDashDash failed' , e ) ;
4673+ }
4674+ } ) ( ) ;
0 commit comments