// ==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);
})();