Greasy Fork is available in English.
菜单按需唤醒,带图钉锁定,新增一键清空垃圾桶功能。
// ==UserScript==
// @name New API 批量模型前缀助手
// @namespace http://tampermonkey.net/
// @version 1.8
// @description 菜单按需唤醒,带图钉锁定,新增一键清空垃圾桶功能。
// @author YourName
// @match *://*/*
// @grant GM_registerMenuCommand
// ==/UserScript==
(function() {
'use strict';
// 1. 创建悬浮容器 (默认彻底隐藏)
const container = document.createElement('div');
container.style.cssText = `
position: fixed;
right: 20px;
top: 100px;
z-index: 99999;
background: rgba(30, 30, 30, 0.9);
backdrop-filter: blur(8px);
border-radius: 8px;
box-shadow: 0 4px 15px rgba(0,0,0,0.4);
border: 1px solid rgba(255, 255, 255, 0.1);
width: 250px;
font-family: sans-serif;
color: #eee;
transition: width 0.3s ease;
display: none;
`;
// 注入 HTML,注意底部按钮区变成了 flex 布局,加入了垃圾桶按钮
container.innerHTML = `
<div id="tm-header" style="padding: 10px 15px; cursor: move; border-bottom: 1px solid rgba(255, 255, 255, 0.1); display: flex; justify-content: space-between; align-items: center; font-weight: bold; font-size: 14px; user-select: none;">
<span>✨ 批量添加前缀</span>
<div style="display: flex; gap: 10px; align-items: center;">
<span id="tm-pin-icon" style="cursor: pointer; font-size: 14px; opacity: 0.4; transition: opacity 0.2s, transform 0.2s;" title="锁定位置">📌</span>
<span id="tm-toggle-icon" style="cursor: pointer; font-size: 12px; opacity: 0.8;" title="折叠/展开">▼</span>
</div>
</div>
<div id="tm-content" style="padding: 15px; display: block;">
<input type="text" id="tm-prefix-input" placeholder="输入前缀 (如 wuer/)" style="width: 100%; margin-bottom: 10px; padding: 8px; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.2); border-radius: 4px; color: #fff; box-sizing: border-box; outline: none;">
<textarea id="tm-models-input" placeholder="输入模型列表 (用换行或逗号分隔)" style="width: 100%; height: 100px; margin-bottom: 10px; padding: 8px; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.2); border-radius: 4px; color: #fff; box-sizing: border-box; resize: vertical; outline: none;"></textarea>
<div style="display: flex; gap: 8px;">
<button id="tm-generate-btn" style="flex: 1; padding: 8px 0; background: #1677ff; color: #fff; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; transition: background 0.3s;">1. 生成并复制</button>
<button id="tm-clear-btn" style="padding: 0 12px; background: rgba(255,255,255,0.1); color: #fff; border: 1px solid rgba(255,255,255,0.2); border-radius: 4px; cursor: pointer; transition: background 0.3s;" title="一键清空输入框">🗑️</button>
</div>
<div style="margin-top: 10px; font-size: 12px; color: #aaa; line-height: 1.4;">2. 在原网页“模型重定向”处点击【手动编辑】,按 Ctrl+V 粘贴。</div>
</div>
`;
document.body.appendChild(container);
// ================== 按需唤醒与记忆功能 (来自 V1.5) ==================
let isPanelVisible = localStorage.getItem('tm_newapi_helper_active') === 'true';
if (isPanelVisible) {
container.style.display = 'block';
}
GM_registerMenuCommand("🚀 启用/关闭 前缀助手 (仅当前网站)", () => {
isPanelVisible = !isPanelVisible;
container.style.display = isPanelVisible ? 'block' : 'none';
if (isPanelVisible) {
localStorage.setItem('tm_newapi_helper_active', 'true');
alert("✅ 已在此网站永久启用前缀助手!");
} else {
localStorage.removeItem('tm_newapi_helper_active');
alert("❌ 已在此网站关闭前缀助手。");
}
});
// ================== 垃圾桶清空逻辑 ==================
document.getElementById('tm-clear-btn').addEventListener('click', () => {
document.getElementById('tm-prefix-input').value = '';
document.getElementById('tm-models-input').value = '';
// 给按钮一个简单的视觉反馈
const clearBtn = document.getElementById('tm-clear-btn');
clearBtn.style.background = "rgba(255,50,50,0.5)";
setTimeout(() => {
clearBtn.style.background = "rgba(255,255,255,0.1)";
}, 300);
});
// ================== 原有交互逻辑 (拖拽、图钉、折叠、复制) ==================
const header = document.getElementById('tm-header');
const content = document.getElementById('tm-content');
const toggleIcon = document.getElementById('tm-toggle-icon');
const pinIcon = document.getElementById('tm-pin-icon');
let isCollapsed = false;
let isPinned = false;
let isDragging = false;
let hasMoved = false;
let offsetX, offsetY;
pinIcon.addEventListener('click', (e) => {
e.stopPropagation();
isPinned = !isPinned;
if (isPinned) {
pinIcon.style.opacity = '1';
pinIcon.style.transform = 'rotate(-45deg)';
header.style.cursor = 'default';
} else {
pinIcon.style.opacity = '0.4';
pinIcon.style.transform = 'rotate(0deg)';
header.style.cursor = 'move';
}
});
header.addEventListener('mousedown', (e) => {
if (isPinned) return;
if (e.target === pinIcon || e.target === toggleIcon) return;
isDragging = true;
hasMoved = false;
offsetX = e.clientX - container.getBoundingClientRect().left;
offsetY = e.clientY - container.getBoundingClientRect().top;
container.style.right = 'auto';
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
hasMoved = true;
container.style.left = (e.clientX - offsetX) + 'px';
container.style.top = (e.clientY - offsetY) + 'px';
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
const toggleCollapse = () => {
isCollapsed = !isCollapsed;
if (isCollapsed) {
content.style.display = 'none';
toggleIcon.innerText = '▲';
container.style.width = '160px';
header.style.borderBottom = 'none';
} else {
content.style.display = 'block';
toggleIcon.innerText = '▼';
container.style.width = '250px';
header.style.borderBottom = '1px solid rgba(255, 255, 255, 0.1)';
}
};
toggleIcon.addEventListener('click', (e) => {
e.stopPropagation();
toggleCollapse();
});
header.addEventListener('click', (e) => {
if (hasMoved || e.target === pinIcon || e.target === toggleIcon) return;
toggleCollapse();
});
document.getElementById('tm-generate-btn').addEventListener('click', () => {
const prefix = document.getElementById('tm-prefix-input').value.trim();
const modelsStr = document.getElementById('tm-models-input').value.trim();
if (!prefix || !modelsStr) {
alert("请确保前缀和模型列表都已填写!");
return;
}
const models = modelsStr.split(/[\n,,]+/).map(m => m.trim()).filter(m => m);
const mapping = {};
models.forEach(model => mapping[prefix + model] = model);
navigator.clipboard.writeText(JSON.stringify(mapping, null, 2)).then(() => {
const btn = document.getElementById('tm-generate-btn');
btn.innerText = "✅ 复制成功!";
btn.style.background = "#52c41a";
setTimeout(() => {
btn.innerText = "1. 生成并复制";
btn.style.background = "#1677ff";
}, 3000);
}).catch(() => alert("复制失败,请重试!"));
});
})();