GitHub issue 输入修复助手(Linux Firefox)

解决 Linux Firefox Github issue 中文输入法无法输入的问题,仅在 issue 页面启用

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         GitHub issue 输入修复助手(Linux Firefox)
// @namespace    https://github.com/
// @version      1.1
// @description  解决 Linux Firefox Github issue 中文输入法无法输入的问题,仅在 issue 页面启用
// @author       ChatGPT
// @match        https://github.com/*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
  'use strict';

  let targetInput;
  let hiddenInput;

  // 使用 MutationObserver 监听页面加载和输入框变化
  const observer = new MutationObserver(() => {
    const isIssuePage = /^\/[^\/]+\/[^\/]+\/issues\/?$/.test(location.pathname);
    //console.log("isIssue",isIssuePage,location.pathname)
    targetInput = document.querySelector('input[role="combobox"][placeholder*="Search"]');

    if (isIssuePage && targetInput && !document.getElementById('hidden-ime-input')) {
      setupRedirectInput();
    } else if (!isIssuePage && document.getElementById('hidden-ime-input')) {
      hideRedirectInput();
    }else{
      hideRedirectInput(false);
    }
  });

  observer.observe(document.body, { childList: true, subtree: true });

  // 设置 React 输入框的值(兼容受控组件)
  function setReactInputValue(input, value) {
    const nativeSetter = Object.getOwnPropertyDescriptor(
      window.HTMLInputElement.prototype, 'value'
    ).set;
    nativeSetter.call(input, value);
    const ev = new Event('input', { bubbles: true });
    input.dispatchEvent(ev);
  }

  function setupRedirectInput() {
    if (!targetInput) return;
    console.log('[IME Redirect] 绑定输入框:', targetInput);

    const rect = targetInput.getBoundingClientRect();
    const style = getComputedStyle(targetInput);

    hiddenInput = document.createElement('input');
    hiddenInput.id = 'hidden-ime-input';

    hiddenInput.style.cssText = `
      position: absolute;
      top: ${window.scrollY + rect.top - 40}px;
      left: ${window.scrollX + rect.left - 10}px;
      width: ${rect.width}px;
      height: ${rect.height}px;
      opacity: 1;
      z-index: 9999;
      font-size: ${style.fontSize};
      border: none;
      background: white;
      outline: 1px solid #ccc;
      border-radius: 5px;
    `;

    hiddenInput.setAttribute('autocomplete', 'off');
    hiddenInput.setAttribute('autocorrect', 'off');
    hiddenInput.setAttribute('autocapitalize', 'off');
    hiddenInput.setAttribute('spellcheck', 'false');

    document.body.appendChild(hiddenInput);

    hiddenInput.value = targetInput.value || '';

    hiddenInput.addEventListener('select', () => {
      targetInput.setSelectionRange(hiddenInput.selectionStart, hiddenInput.selectionEnd);
    });

    hiddenInput.addEventListener('input', () => {
      setReactInputValue(targetInput, hiddenInput.value);
    });

    let focus = false;
    let lastValue = targetInput.value;

    setInterval(() => {
      const isIssuePage = /^\/[^\/]+\/[^\/]+\/issues\/?$/.test(location.pathname);
      if (!isIssuePage) {
        hideRedirectInput();
        return;
      }

      if(!targetInput) return;

      if (targetInput.value !== lastValue && !focus) {
        lastValue = targetInput.value;
        hiddenInput.value = targetInput.value || '';

        try {
          hiddenInput.selectionStart = targetInput.selectionStart;
          hiddenInput.selectionEnd = targetInput.selectionEnd;
        } catch {}
      }
    }, 100);

    hiddenInput.addEventListener('focus', () => {
      focus = true;
    });

    hiddenInput.addEventListener('blur', () => {
      focus = false;
    });

    hiddenInput.addEventListener('keydown', (e) => {
      if (e.key === 'Enter') {
        simulateEnterKeyOnTarget(targetInput);
        e.preventDefault();
      }
    });

    console.log("显示")

    // 可选:初始自动聚焦
    // hiddenInput.focus();
  }

  function hideRedirectInput(hide=true) {
    const existing = document.getElementById('hidden-ime-input');
    if (existing) {
        if(hide){
            existing.style.display = 'none';
        }else{
            existing.style.display = 'block';
        }
    }
  }

  function simulateEnterKeyOnTarget(input) {
    const keyboardEventInit = {
      key: 'Enter',
      code: 'Enter',
      keyCode: 13,
      which: 13,
      bubbles: true,
      cancelable: true
    };

    const down = new KeyboardEvent('keydown', keyboardEventInit);
    const up = new KeyboardEvent('keyup', keyboardEventInit);

    input.dispatchEvent(down);
    input.dispatchEvent(up);
  }

})();