Greasy Fork

Greasy Fork is available in English.

New API 批量模型前缀助手

菜单按需唤醒,带图钉锁定,新增一键清空垃圾桶功能。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==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("复制失败,请重试!"));
    });
})();