// ==UserScript== // @name 标签页面管理 // @namespace https://viayoo.com/jcdamz // @description 管理链接跳转行为(新标签/同页)、禁止页面重载、黑白名单切换,需要GM环境,非GM环境目前不考虑匹配。 // @version 1.1 // @author Via & Gemini // @match *://*/* // @license MIT // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAYhSURBVHic7Z1fbBRVFMa/M7stFG1rhJq22y79C21FLQmlfbM8mGhiYl/UxJgI/onQRCw+4gPy4IO+IJAIRqVITEgMwWJixBAFLCnWLbYQoJUCaYGllUVot9Jut9s9Pixst9Kl2507e3fL+SXbzHTvfOfM/Wbm3pnJvQsIgiAIgiAIgiAIgiAIgiAIgiAIwnyGEhmsoKLm2UTGi5drPa7jiYplqQEFFavqDaKXCEYDCEVWxlIOo48RbAkyH7rW03HMqjCWGFBQsareRrYtINRboZ9wGMcmeXKrFUbYVAs6q1ZvMcjYm3JH/IMgFBlkrM3OcWDY41Z6eVJqgLOqthlMTSAGJbZ5sRwGA6D67JyComGP+5AqXWUGOKtqm4mxlghJWfnMAIgBjmNjCv2h0GK1ShOU1JSzsraJCNvurRcW5GPl0yuilj924iS83hEVoWOCme/WobndjdRhxqYr3e2fmc3NMCuQW1FXRIRtHHFkTUwEzMoqg5lBRKYrHwCICEyhs4kI23Ir6ky3c6azWlq5+nsQNYR3NIlgBgg8rfInSx1z1jHcHpDPH6Eb2ldm/uZK9x9rzeRoqsYcVTXVdhidkUnNhN2ehkBgwkyouPh/TmPrXkTwqdKYtn0+x4EnH80Or9vbzoBGRgEAR379DT0XLgEAxoNUPNjze1+8Odrj3RAAbGzUz2ahs6gMy6uqMeH349wZFzw3BsyEnBsRuU3mL4m58gHglVwnns+JOFuKq8KLlcvK0PjBhwCAdOIGAHG3BaYMIHDDbCdRSXko8bT0dDiLyxNrQCQZCwAAWfY0bC1/BoULH5l1k6P/DKLZfQmjkwGQ+wYWtLTi2692ICsrM1wmVAeaDADRrM92xkZHkZadHl7WzTuF5Xg1L/a289itv/Hl1V4Y168ho6Pz/gIx1MGDMGdADJw+1QZncTkA4HLveavDKSfLnmapvuUG+HyjuNB92uowKYvp+4AwSdYFTRXUGcDx3OOnHu0dnTj4w0/K9JRdgggUes4yz0+E19/eqFRP4SXo7ieup10PLxY0woQXnluD0pKl6qXnSHtHJ1ynpncArvruzEnj3L/DKlO6D0t6QaUlS/H+hreskJ4TO3bvuc+A7wb64Q0Epj1miMbJIQ/abnusSg9AArqhyQLdmnr8fdjjxmGPe47be1WnBEBlG5CEVCwrCy8bt70g9424texnL4eXWWGPb16fAZXLy6etZ3x+EIEVJeDHs+akYzt7CTb3zal/KOzpzWsDCh15eG/9Ouzc3QwAoLFxpLm6TWlOvRUznx8wzy9BALBx/Zt447WXlWhx6A2P0rv+hJwB4xOTuOPT95qyccO7qK2txfHWNvRevIg/u87EvK0jLxeVFeXw+Xw4cdKlPLeEGGAzCGk2vbfIdTUrUVezMubyNpuBRQumqmf7rq9T1wC7zUDmovREhEo55n0bkOyIAZoRAzQjBmhGDNCMGKAZMUAzYoBmxADNiAGaEQM0IwZoRgzQjBigGTFAM2KAZsQAzYgBmhEDNCMGaEYM0IwYoBkxQDNigGYsMaD7r4tWyGrFqn2yxICeeWiAVftkzgDmGQdQuQcGEzohk9V4vSNwDwzO/CWj34y2KQOYKOosgtt37TEjnVQ8aF8YbGomRXMGBIMt0b7bt/8A2mea3CLFaO/oxL79B6J+z6zRAJtvoiXaZQgAGjdtxpGjrWZCaOXI0VY0btocvQDzsM03EfUgjAVTsyYODQ36sp9wLCTQjBO0jvv9+PHnX9Dd04ucJYtRkJ9nJlzCaO/oxMef7sDOL5ox7vdHLceET/p7Tx02E8v0qImiourHOCO9D0SzD7wFULm8DFmZmbMX1IB3ZCT27ibzMI35i/r6uobMxFQybCVy7riHAubhAHG9+7yry6yUkvsA93lXVzAYXKdCKxUIMjepqHxA4cy53pvXu7IW5/cTsAZEC1XpJhXMw0HmDVd7XHtVSSofOeeoqqm2s9ECgv7ZOlTC6A9QsEHVkX8Py4YuOitrmwj8UayNc9ISOuqbVB71kVg+djT0Iw62BgJXhyKam2XQcpiPAwCDuoI82WLljzcIgiAIgiAIgiAIgiAkmP8ABazsbkReAE4AAAAASUVORK5CYII= // @run-at document-start // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // ==/UserScript== (function() { 'use strict'; const KEY = 'page_manager_configs'; const defaultSettings = { activeTab: 1, newTab: { enabled: true, mode: 'white', list: '' }, removeNewTab: { enabled: false, mode: 'white', list: '' }, preventReload: { enabled: false, mode: 'white', list: '' } }; let settings = GM_getValue(KEY, defaultSettings); let shadowRoot = null; const checkMatch = (config) => { if (!config.enabled) return false; const currentUrl = location.href; const lines = config.list.split('\n').map(l => l.trim()).filter(Boolean); let isMatched = false; for (let line of lines) { try { if (line.startsWith('/') && line.endsWith('/')) { const parts = line.slice(1, -1).split('/'); const flags = parts.length > 1 ? parts.pop() : ''; const regex = new RegExp(parts.join('/'), flags); if (regex.test(currentUrl)) { isMatched = true; break; } } else if (currentUrl.includes(line)) { isMatched = true; break; } } catch (e) { console.error('Regex error:', e); } } return config.mode === 'white' ? isMatched : !isMatched; }; if (checkMatch(settings.newTab)) { window.addEventListener('click', (e) => { const link = e.target.closest('a'); if (link && link.href && /^https?:\/\//i.test(link.href)) { e.preventDefault(); window.open(link.href, '_blank'); } }, true); } if (checkMatch(settings.removeNewTab)) { let timer = null; const cleanTags = () => { document.querySelectorAll('a[target="_blank"]').forEach(a => a.removeAttribute('target')); }; const throttledClean = () => { if (timer) return; timer = requestAnimationFrame(() => { cleanTags(); timer = null; }); }; cleanTags(); new MutationObserver(throttledClean).observe(document.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['target'] }); } if (checkMatch(settings.preventReload)) { const freeze = (obj, prop, value) => { try { Object.defineProperty(obj, prop, { configurable: false, writable: false, value: value }); } catch (e) {} }; freeze(document, 'visibilityState', 'visible'); freeze(document, 'hidden', false); window.addEventListener('visibilitychange', e => e.stopImmediatePropagation(), true); window.addEventListener('pageshow', (event) => { if (event.persisted) { console.log('Loaded from BFCache'); } }, true); } function createUI() { if (shadowRoot) return; const container = document.createElement('div'); container.style.cssText = 'position:fixed;top:0;left:0;z-index:2147483647;'; document.documentElement.appendChild(container); shadowRoot = container.attachShadow({ mode: 'closed' }); const style = document.createElement('style'); style.textContent = ` .mask { position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.4); backdrop-filter:blur(10px); display:flex; align-items:center; justify-content:center; animation: fade 0.3s; pointer-events:auto; font-family:system-ui,-apple-system; } .panel { background:rgba(255,255,255,0.9); width:340px; border-radius:30px; padding:20px; box-shadow:0 20px 40px rgba(0,0,0,0.2); position:relative; overflow:hidden; } .header { margin-bottom: 18px; padding-bottom: 15px; border-bottom: 1px solid rgba(0,0,0,0.05); } .domain { font-size: 18px; font-weight: 700; color: #1C2526; margin-bottom: 4px; display: block; } .url-bar { display: flex; align-items: center; gap: 8px; background: rgba(0,0,0,0.03); padding: 6px 10px; border-radius: 10px; overflow: hidden; } .url-text { font-size: 11px; color: #888; white-space: nowrap; overflow: hidden; flex: 1; mask-image: linear-gradient(to right, black 85%, transparent 100%); } .url-text:hover { overflow: visible; animation: scroll 8s linear infinite; } .copy-btn { font-size: 10px; background: #fff; border: 1px solid #ddd; padding: 2px 6px; border-radius: 6px; cursor: pointer; color: #007AFF; } .status-indicators { display: flex; gap: 6px; margin-top: 10px; } .dot { width: 12px; height: 12px; border-radius: 4px; background: rgba(0,0,0,0.05); transition: 0.3s; position: relative; } .dot.active { background: #34C759; box-shadow: 0 0 8px rgba(52, 199, 89, 0.4); } .dot::after { content: attr(data-tip); position: absolute; top: 18px; left: 50%; transform: translateX(-50%); font-size: 9px; color: #999; white-space: nowrap; opacity: 0; transition: 0.2s; } .dot:hover::after { opacity: 1; } @keyframes scroll { 0% { transform: translateX(0); } 100% { transform: translateX(-100%); } } .nav { display:flex; gap:5px; margin-bottom:15px; background:rgba(0,0,0,0.05); padding:5px; border-radius:15px; } .nav-item { flex:1; padding:8px; text-align:center; font-size:12px; cursor:pointer; border-radius:10px; transition:0.3s; font-weight:600; color:#666; } .nav-item.active { background:#fff; color:#007AFF; box-shadow:0 2px 8px rgba(0,0,0,0.1); } .content-wrapper { transition: 0.3s ease; } .section { display:none; flex-direction:column; gap:12px; animation: slide 0.3s; } .section.active { display:flex; } .row { display:flex; justify-content:space-between; align-items:center; } .label { font-size:14px; font-weight:600; color:#333; } .switch-group { display:flex; gap:8px; } .btn { border:none; padding:6px 12px; border-radius:8px; cursor:pointer; font-size:12px; transition:0.2s; background:#eee; } .btn-mode.active { background:#007AFF; color:#fff; } .btn-toggle.on { background:#34C759; color:#fff; } textarea { width:100%; height:100px; border-radius:12px; border:1px solid #ddd; padding:10px; box-sizing:border-box; font-family:monospace; font-size:12px; resize:none; transition:0.3s; } textarea:focus { outline:none; border-color:#007AFF; box-shadow:0 0 0 3px rgba(0,122,255,0.1); } textarea.error { border-color:#ff4d4f; background:#fff1f0; } textarea.conflict-highlight { border-color: #FF9500; box-shadow: 0 0 0 3px rgba(255,149,0,0.2); animation: shake 0.4s; } .footer { margin-top:15px; display:flex; flex-direction:column; gap:8px; } .save-btn { background:#007AFF; color:#fff; border:none; padding:12px; border-radius:15px; font-weight:bold; cursor:pointer; } .import-btn { background:#34C759; color:#fff; border:none; padding:8px; border-radius:10px; font-weight:bold; cursor:pointer; margin-top:5px; } .conflict-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255,59,48,0.95); color: #fff; z-index: 100; display: none; flex-direction: column; align-items: center; justify-content: center; padding: 30px; box-sizing: border-box; text-align: center; backdrop-filter: blur(5px); } .conflict-layer.show { display: flex; animation: fade 0.3s; } .conflict-msg { font-size: 14px; line-height: 1.6; margin-bottom: 20px; } .conflict-guide { font-size: 12px; opacity: 0.8; margin-top: 10px; border-top: 1px solid rgba(255,255,255,0.3); padding-top: 10px; } @keyframes fade { from { opacity:0; } to { opacity:1; } } @keyframes slide { from { transform:translateX(10px); opacity:0; } to { transform:translateX(0); opacity:1; } } @keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-4px); } 75% { transform: translateX(4px); } } @media (prefers-color-scheme: dark) { .panel { background:rgba(30,30,30,0.9); color:#fff; } .label, .domain { color:#ddd; } textarea { background:#222; border-color:#444; color:#eee; } .nav-item.active { background:#444; color:#0A84FF; } .url-bar { background: rgba(255,255,255,0.05); } .copy-btn { background: #333; border-color: #555; } } `; shadowRoot.appendChild(style); const mask = document.createElement('div'); mask.className = 'mask'; const panel = document.createElement('div'); panel.className = 'panel'; const getCleanUrl = () => { try { const url = new URL(location.href); const paramsToKeep = ['id', 'pg', 'p']; const searchParams = new URLSearchParams(); url.searchParams.forEach((value, key) => { if (paramsToKeep.includes(key.toLowerCase()) || key.length < 3) { searchParams.append(key, value); } }); url.search = searchParams.toString(); return url.toString(); } catch (e) { return location.href; } }; const render = () => { panel.innerHTML = `