wujie icon indicating copy to clipboard operation
wujie copied to clipboard

子应用monaco editor点击事件不生效

Open StrangeXin opened this issue 3 years ago • 6 comments

bug描述 子应用里面有monaco editor功能,里面的代码无法编辑

如何复现 给出详细的复现步骤 可以直接拿monaco官网测试,https://microsoft.github.io/monaco-editor/ 将链接嵌入进来,点击中间的代码光标没有出现,但是光标选在代码最后是可以的,添加新的代码也是可以的

错误截图 无界-无界

排查了不是跨域的问题,也不是资源加载的问题,看了下monaco.current.onMouseDown(e)返回的e.target里面是空,但是e.event是正确的内容,还没找到具体是什么原因,请大佬排查一下,用无界把编辑器嵌入主应用来这种需求还是比较常见的

StrangeXin avatar Nov 26 '22 15:11 StrangeXin

看看 #205 呢

yiludege avatar Nov 26 '22 16:11 yiludege

已解决,感谢

StrangeXin avatar Nov 28 '22 02:11 StrangeXin

image image

同样的问题,谷歌浏览器解决了,但firefox浏览器13.0.1 版本下仍然不可点击

StrangeXin avatar May 17 '23 02:05 StrangeXin

升级monaco版本可以解决 "monaco-editor": "^0.29.0", "monaco-editor-webpack-plugin": "^5.0.0", 但是firefox还是会有同样问题,可以尝试继续升级吧,后续我没尝试

PiuQiuPiaQia avatar May 22 '23 06:05 PiuQiuPiaQia

你好 你是怎么解决的 能给个方案吗 有偿 合适请加下企鹅 1036752557 教一下我

Caoxiongk avatar Aug 21 '23 02:08 Caoxiongk

import fs from 'fs'
import path from 'path'

/**
 * Monaco Editor 0.52.0 + Wujie 最终补丁方案
 *
 * 问题根源:
 * Monaco的_actualDoHitTestWithCaretRangeFromPoint函数内部有Wujie特殊处理,
 * 但这个处理有bug - 备用函数f在Wujie环境下返回的Range对象不正确。
 *
 * 解决方案:
 * 不依赖备用函数f,直接让Monaco在Wujie环境下使用document.caretRangeFromPoint,
 * 然后我们hijack document.caretRangeFromPoint来支持shadowDOM。
 */
export const patchMonacoEditor = () => {
  try {
    const cwd = process.cwd()
    const filePath = path.resolve(cwd, 'node_modules/monaco-editor/min/vs/editor/editor.main.js')

    if (!fs.existsSync(filePath)) {
      console.log('❌ Monaco editor file not found:', filePath)
      return
    }

    let content = fs.readFileSync(filePath, 'utf8')

    // 创建备份
    const backupPath = `${filePath}.backup`
    if (!fs.existsSync(backupPath)) {
      fs.writeFileSync(backupPath, content)
      console.log('✅ 已备份Monaco Editor原始文件')
    }

    let modified = false

    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    // 修复方案:Hijack document.caretRangeFromPoint 支持shadowDOM
    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    const hijackCode = `
;(function(){
if(typeof window!=='undefined'&&window.__POWERED_BY_WUJIE__&&window.__WUJIE&&window.__WUJIE.shadowRoot){
const sr=window.__WUJIE.shadowRoot;

// Hijack document.caretRangeFromPoint to support Wujie shadowDOM
if(!Document.prototype.__originalCaretRangeFromPoint){
  Document.prototype.__originalCaretRangeFromPoint=Document.prototype.caretRangeFromPoint;
  
  Document.prototype.caretRangeFromPoint=function(x,y){
    try{
      // 首先在shadowRoot中查找元素
      let el=sr.elementFromPoint(x,y);
      if(!el){
        // 回退到原始实现
        return this.__originalCaretRangeFromPoint.call(this,x,y);
      }
      
      // 创建range
      const range=this.createRange();
      
      // 遍历找到文本节点
      while(el&&el.firstChild){
        if(el.firstChild.nodeType===3)break;//TEXT_NODE
        el=el.firstChild;
      }
      
      // 获取文本节点
      let textNode=el&&el.nodeType===3?el:el&&el.firstChild;

      // 如果没有文本节点,尝试使用el本身
      if(!textNode||textNode.nodeType!==3){
        if(el&&el.nodeType===3){
          textNode=el;
        }else if(el&&el.textContent){
          // 最后尝试:获取el的任意文本子节点
          const walker=this.createTreeWalker(el,NodeFilter.SHOW_TEXT);
          textNode=walker.nextNode();
        }
      }
      
      // 如果还是没有文本节点,返回null
      if(!textNode||textNode.nodeType!==3){
        return null;
      }
      
      // 🎯 精确计算offset(基于像素位置)
      const text=textNode.textContent||'';
      let offset=0;
      
      if(text.length>0){
        // 使用临时Range测量每个字符的位置
        const testRange=this.createRange();
        
        for(let i=0;i<text.length;i++){
          testRange.setStart(textNode,i);
          testRange.setEnd(textNode,i+1);
          
          const charRect=testRange.getBoundingClientRect();
          const charMidpoint=charRect.left+charRect.width/2;
          
          // 如果鼠标在字符左半部分,offset就是i
          if(x<charMidpoint){
            offset=i;
            break;
          }
          // 否则offset继续向后
          offset=i+1;
        }
      }
      
      range.setStart(textNode,offset);
      range.setEnd(textNode,offset);
      return range;
    }catch(err){
      console.error('[Monaco+Wujie] caretRangeFromPoint error:',err);
      // 出错时回退到原始实现
      return this.__originalCaretRangeFromPoint.call(this,x,y);
    }
  };
  
  console.log('✅ Monaco+Wujie: Hijacked document.caretRangeFromPoint for shadowDOM support');
}
}
})();
`

    // 在文件开头注入
    if (!content.includes('__originalCaretRangeFromPoint')) {
      content = hijackCode + content
      modified = true
      console.log('✅ 注入document.caretRangeFromPoint hijack代码')
    }

    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    // 修复MARK正则兼容性
    // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    // const markRegexStr = 'new RegExp("\\bMARK:\\s*(.*)$","d")'
    // if (content.includes(markRegexStr)) {
    //   const markRegexReplacement = `new RegExp("\\bMARK:\\s*(.*)$", (()=>{try{new RegExp('','d');return 'd';}catch(e){return '';}})())`
    //   content = content.replace(markRegexStr, markRegexReplacement)
    //   modified = true
    //   console.log('✅ 修复MARK正则兼容性')
    // }

    if (modified) {
      fs.writeFileSync(filePath, content)
      console.log('')
      console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
      console.log('🎉 Monaco Editor补丁应用成功')
      console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
      console.log('')
      console.log('补丁内容:')
      console.log('  ✓ Hijack document.caretRangeFromPoint')
      console.log('  ✓ 在shadowRoot中查找元素')
      console.log('  ✓ 创建有效的Range对象')
      console.log('  ✓ 完整的错误处理和fallback')
      console.log('')
      console.log('工作原理:')
      console.log('  Monaco调用document.caretRangeFromPoint(x, y)')
      console.log('  → 我们的hijack使用shadowRoot.elementFromPoint(x, y)')
      console.log('  → 遍历找到文本节点')
      console.log('  → 创建有效的range对象')
      console.log('  → Monaco后续处理这个range')
      console.log('')
    } else {
      console.log('⚠️ Monaco Editor 补丁未应用任何修改或已存在')
    }
  } catch (error) {
    console.error('❌ Monaco Editor补丁失败:', error)
  }
}

WuQic avatar Sep 30 '25 07:09 WuQic