因为移动端上没有Shlink插件,我让ai写了一个油猴版本的

目前看来还不错,就是图标略大了点

继续修改,小了一点,可以按域名保存位置了

// ==UserScript==// @name         Shlink 一键生成短链接 (记忆位置版)// @namespace    http://tampermonkey.net/// @version      1.4// @description  支持 LocalStorage 记忆每个域名的按钮位置,迷你可拖动// @author       Gemini// @match        *://*/*// @grant        GM_xmlhttpRequest// @grant        GM_setClipboard// @connect      curl.su// ==/UserScript==(function() {    'use strict';    // --- 配置信息 ---    const SHLINK_DOMAIN = 'https://curl.su';     const API_KEY = '你的-API-KEY';             const TAG_NAME = 'from-tampermonkey';     const STORAGE_KEY = 'shlink_btn_pos'; // LocalStorage 存储键名    // ----------------    // 读取存储的位置(如果没有则使用默认值)    const savedPos = JSON.parse(localStorage.getItem(STORAGE_KEY)) || { bottom: 80, right: 20 };    const container = document.createElement('div');    container.style = `        position: fixed; bottom: ${savedPos.bottom}px; right: ${savedPos.right}px; z-index: 10000;        display: flex; flex-direction: column; align-items: center;        touch-action: none;    `;    document.body.appendChild(container);    const toast = document.createElement('div');    toast.style = `        background: rgba(0, 0, 0, 0.75); color: white; padding: 5px 10px;        border-radius: 6px; font-size: 11px; margin-bottom: 5px;        display: none; white-space: nowrap; pointer-events: none;        box-shadow: 0 2px 8px rgba(0,0,0,0.2);    `;    container.appendChild(toast);    const btn = document.createElement('div');    btn.innerHTML = '🔗';    btn.style = `        width: 35px; height: 35px; background: #007bff; color: white;        text-align: center; line-height: 35px; border-radius: 50%;        cursor: move; box-shadow: 0 3px 10px rgba(0,0,0,0.3); font-size: 16px;        user-select: none; -webkit-tap-highlight-color: transparent;        opacity: 0.8;    `;    container.appendChild(btn);	    // --- 拖拽逻辑 ---    let isDragging = false;    let startX, startY, initialRight, initialBottom;    const startDrag = (e) => {        isDragging = false;        const touch = e.type === 'touchstart' ? e.touches[0] : e;        startX = touch.clientX;        startY = touch.clientY;        const rect = container.getBoundingClientRect();        initialRight = window.innerWidth - rect.right;        initialBottom = window.innerHeight - rect.bottom;                 document.addEventListener(e.type === 'touchstart' ? 'touchmove' : 'mousemove', onDrag);        document.addEventListener(e.type === 'touchstart' ? 'touchend' : 'mouseup', stopDrag);    };    const onDrag = (e) => {        const touch = e.type === 'touchmove' ? e.touches[0] : e;        const deltaX = startX - touch.clientX;        const deltaY = startY - touch.clientY;                if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {            isDragging = true;            const finalRight = initialRight + deltaX;            const finalBottom = initialBottom + deltaY;            container.style.right = finalRight + 'px';            container.style.bottom = finalBottom + 'px';                        // 实时或停止后保存位置            localStorage.setItem(STORAGE_KEY, JSON.stringify({ right: finalRight, bottom: finalBottom }));        }    };    const stopDrag = (e) => {        document.removeEventListener(e.type === 'touchend' ? 'touchmove' : 'mousemove', onDrag);        document.removeEventListener(e.type === 'touchend' ? 'touchend' : 'mouseup', stopDrag);    };    btn.addEventListener('mousedown', startDrag);    btn.addEventListener('touchstart', startDrag, {passive: true});    // --- API 请求逻辑 ---    function showToast(msg) {        toast.innerText = msg;        toast.style.display = 'block';        setTimeout(() => { toast.style.display = 'none'; }, 2000);    }    btn.onclick = function() {        if (isDragging) return;        btn.innerHTML = '⏳';        GM_xmlhttpRequest({            method: "POST",            url: `${SHLINK_DOMAIN}/rest/v3/short-urls`,            headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" },            data: JSON.stringify({ "longUrl": window.location.href, "findIfExists": true, "tags": [TAG_NAME] }),            onload: function(response) {                btn.innerHTML = '🔗';                const data = JSON.parse(response.responseText);                if (data.shortUrl) {                    GM_setClipboard(data.shortUrl);                    showToast(`已复制`);                } else { showToast("失败"); }            },            onerror: () => { btn.innerHTML = '🔗'; showToast("错误"); }        });    };})();