// ==UserScript== // @name OpenNana 免费提示词一键复制(解锁登录限制) // @namespace doubao-opennana-helper // @version 1.0.0 // @description 解除 opennana.com 免费提示词“复制需登录”限制:点击“复制”直接复制中/英文提示词,自动关掉登录弹窗,可自由选中文字;带浮动面板开关与状态提示。 // @author you // @match https://opennana.com/* // @run-at document-idle // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @noframes // @license GPL-3.0 // ==/UserScript== (function () { 'use strict'; var KEY = 'opennana_copy_unlock'; // 默认开启;状态存本地,刷新后记住 var enabled = GM_getValue(KEY, true); var panelId = 'on-unlock-panel'; var toastId = 'on-unlock-toast'; // ---------- 工具 ---------- function copyText(text) { if (navigator.clipboard && window.isSecureContext) { return navigator.clipboard.writeText(text); } // 兜底:旧浏览器用隐藏文本框 + execCommand var ta = document.createElement('textarea'); ta.value = text; ta.style.position = 'fixed'; ta.style.top = '-9999px'; ta.style.opacity = '0'; document.body.appendChild(ta); ta.focus(); ta.select(); try { document.execCommand('copy'); } catch (e) {} document.body.removeChild(ta); return Promise.resolve(); } // 从“复制”按钮向上找最近的
 提示词文本
  function promptFromBtn(btn) {
    var el = btn;
    for (var i = 0; i < 6 && el; i++) {
      if (el.querySelector) {
        var pre = el.querySelector('pre');
        if (pre && pre.textContent && pre.textContent.trim()) {
          return pre.textContent;
        }
      }
      el = el.parentElement;
    }
    return null;
  }

  function isCopyBtn(node) {
    return node && node.tagName === 'BUTTON' && (node.textContent || '').trim() === '复制';
  }

  // ---------- 提示 toast ----------
  function showToast(msg, ok) {
    var t = document.getElementById(toastId);
    if (!t) {
      t = document.createElement('div');
      t.id = toastId;
      t.style.cssText = 'position:fixed;top:24px;left:50%;transform:translateX(-50%);z-index:999999;padding:10px 18px;border-radius:10px;font-size:14px;font-family:-apple-system,"Microsoft YaHei",sans-serif;box-shadow:0 6px 24px rgba(0,0,0,.18);transition:opacity .3s;pointer-events:none;';
      document.body.appendChild(t);
    }
    t.textContent = msg;
    t.style.background = ok ? '#16a34a' : '#dc2626';
    t.style.color = '#fff';
    t.style.opacity = '1';
    clearTimeout(t._timer);
    t._timer = setTimeout(function () { t.style.opacity = '0'; }, 2000);
  }

  // ---------- 核心:拦截复制按钮(捕获阶段,先于站点逻辑执行) ----------
  function onDocClick(e) {
    if (!enabled) return;
    var node = e.target;
    var btn = node && node.closest ? node.closest('button') : null;
    if (!btn || !isCopyBtn(btn)) return;

    var text = promptFromBtn(btn);
    if (!text) return;

    e.preventDefault();
    e.stopImmediatePropagation(); // 阻止站点打开登录弹窗

    copyText(text).then(function () {
      showToast('已复制提示词(' + text.length + ' 字)', true);
    }).catch(function () {
      showToast('复制失败,请手动选中文字复制', false);
    });
  }
  document.addEventListener('click', onDocClick, true);

  // ---------- 自动关闭登录弹窗 ----------
  function dismissLogin() {
    if (!enabled) return;
    // 找“稍后再说”按钮,点击关闭;找不到则隐藏遮罩容器
    var later = document.querySelector('button');
    var all = document.querySelectorAll('button');
    for (var i = 0; i < all.length; i++) {
      if ((all[i].textContent || '').trim() === '稍后再说') { later = all[i]; break; }
    }
    if (later && (later.textContent || '').trim() === '稍后再说') {
      later.click();
      return true;
    }
    // 兜底:隐藏 fixed 遮罩(含“立即登录/注册”的容器)
    var btns = document.querySelectorAll('button');
    for (var j = 0; j < btns.length; j++) {
      if ((btns[j].textContent || '').trim() === '立即登录 / 注册') {
        var el = btns[j];
        for (var k = 0; k < 6 && el; k++) {
          var cs = el.style || {};
          if (el.classList && /fixed/.test(el.className) && getComputedStyle(el).position === 'fixed') {
            el.style.display = 'none';
            return true;
          }
          el = el.parentElement;
        }
      }
    }
    return false;
  }

  var mo = new MutationObserver(function () {
    if (enabled) dismissLogin();
  });
  function startObserver() {
    mo.observe(document.body, { childList: true, subtree: true });
  }

  // ---------- 让提示词始终可选中 ----------
  function forceSelectable() {
    if (!enabled) return;
    var pres = document.querySelectorAll('pre');
    for (var i = 0; i < pres.length; i++) {
      pres[i].style.userSelect = 'text';
      pres[i].style.webkitUserSelect = 'text';
    }
  }
  // 站点动态渲染,观察器里顺带刷新
  var mo2 = new MutationObserver(function () {
    if (enabled) { forceSelectable(); dismissLogin(); }
  });
  function startObservers() {
    mo2.observe(document.body, { childList: true, subtree: true });
  }

  // ---------- 浮动面板(开关 + 状态) ----------
  function buildPanel() {
    var old = document.getElementById(panelId);
    if (old) old.remove();

    var p = document.createElement('div');
    p.id = panelId;
    p.style.cssText = 'position:fixed;right:16px;bottom:16px;z-index:999998;width:232px;background:#fff;border:1px solid #e5e7eb;border-radius:14px;box-shadow:0 10px 30px rgba(0,0,0,.16);font-family:-apple-system,"Microsoft YaHei",sans-serif;font-size:13px;color:#111827;overflow:hidden;';

    var head = document.createElement('div');
    head.style.cssText = 'display:flex;align-items:center;justify-content:space-between;padding:10px 12px;background:#0f172a;color:#fff;';
    head.innerHTML = 'OpenNana 复制解锁';
    var closeBtn = document.createElement('button');
    closeBtn.textContent = '×';
    closeBtn.style.cssText = 'border:none;background:transparent;color:#cbd5e1;font-size:18px;line-height:1;cursor:pointer;padding:0 2px;';
    closeBtn.addEventListener('click', function () { p.style.display = 'none'; });
    head.appendChild(closeBtn);
    p.appendChild(head);

    var body = document.createElement('div');
    body.style.cssText = 'padding:12px;';

    // 开关行
    var row = document.createElement('div');
    row.style.cssText = 'display:flex;align-items:center;justify-content:space-between;';
    var label = document.createElement('span');
    label.textContent = '一键复制';
    label.style.cssText = 'font-weight:500;';
    var sw = document.createElement('label');
    sw.style.cssText = 'position:relative;display:inline-block;width:44px;height:24px;cursor:pointer;';
    var cb = document.createElement('input');
    cb.type = 'checkbox';
    cb.checked = enabled;
    cb.style.cssText = 'opacity:0;width:0;height:0;position:absolute;';
    var slider = document.createElement('span');
    slider.style.cssText = 'position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:#cbd5e1;border-radius:24px;transition:.25s;';
    var dot = document.createElement('span');
    dot.style.cssText = 'position:absolute;height:18px;width:18px;left:3px;bottom:3px;background:#fff;border-radius:50%;transition:.25s;box-shadow:0 1px 3px rgba(0,0,0,.3);';
    slider.appendChild(dot);
    sw.appendChild(cb);
    sw.appendChild(slider);
    function paintSwitch() {
      if (cb.checked) {
        slider.style.background = '#16a34a';
        dot.style.transform = 'translateX(20px)';
      } else {
        slider.style.background = '#cbd5e1';
        dot.style.transform = 'translateX(0)';
      }
    }
    paintSwitch();
    cb.addEventListener('change', function () {
      enabled = cb.checked;
      GM_setValue(KEY, enabled);
      paintSwitch();
      updateStatus();
      showToast(enabled ? '已开启:复制解锁生效' : '已关闭:恢复原样', true);
      if (!enabled) {
        // 关闭时让可能还挂着的登录遮罩自己决定;不做处理
      }
    });
    row.appendChild(label);
    row.appendChild(sw);
    body.appendChild(row);

    // 状态行
    var status = document.createElement('div');
    status.id = panelId + '-status';
    status.style.cssText = 'margin-top:10px;display:flex;align-items:center;gap:6px;font-size:12px;color:#374151;';
    body.appendChild(status);

    // 提示
    var hint = document.createElement('div');
    hint.style.cssText = 'margin-top:8px;font-size:11px;color:#9ca3af;line-height:1.5;';
    hint.innerHTML = '点击提示词下方的「复制」按钮即可直接复制中英文提示词,登录弹窗会被自动关闭。';
    body.appendChild(hint);

    p.appendChild(body);
    document.body.appendChild(p);

    function updateStatus() {
      var dotEl = document.createElement('span');
      var s = document.getElementById(panelId + '-status');
      s.innerHTML = '';
      var c = document.createElement('span');
      c.style.cssText = 'width:8px;height:8px;border-radius:50%;background:' + (enabled ? '#16a34a' : '#9ca3af') + ';';
      s.appendChild(c);
      s.appendChild(document.createTextNode(enabled ? '已启用 · 复制解锁生效中' : '已禁用 · 站点原始行为'));
    }
    updateStatus();

    // 让面板可拖动(轻量)
    var dragging = false, sx, sy;
    head.addEventListener('mousedown', function (ev) {
      dragging = true; sx = ev.clientX; sy = ev.clientY;
    });
    document.addEventListener('mousemove', function (ev) {
      if (!dragging) return;
      var nx = ev.clientX - sx, ny = ev.clientY - sy;
      p.style.right = 'auto';
      p.style.bottom = 'auto';
      p.style.left = (p.offsetLeft + nx) + 'px';
      p.style.top = (p.offsetTop + ny) + 'px';
      sx = ev.clientX; sy = ev.clientY;
    });
    document.addEventListener('mouseup', function () { dragging = false; });
  }

  // ---------- 初始化 ----------
  function init() {
    if (!document.body) { setTimeout(init, 300); return; }
    buildPanel();
    startObservers();
    forceSelectable();
    dismissLogin();
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }

  // 附加:油猴菜单命令,方便开关
  function menuToggle() {
    enabled = !enabled;
    GM_setValue(KEY, enabled);
    var cb = document.querySelector('#' + panelId + ' input[type=checkbox]');
    if (cb) cb.checked = enabled;
    showToast(enabled ? '已开启:复制解锁生效' : '已关闭:恢复原样', true);
    if (typeof updatePanelStatus === 'function') updatePanelStatus();
  }
  GM_registerMenuCommand('切换 复制解锁 开/关', menuToggle);
})();