// ==UserScript== // @name Cookies Inspector // @namespace https://scriptcat.org/zh-CN/users/184036 // @version 1.0.2 // @description 查看、复制、删除当前网站 Cookies,Via 风格弹窗 // @author DeepSeek // @license MIT // @match *://*/* // @grant GM_registerMenuCommand // @grant GM_setClipboard // @run-at document-idle // ==/UserScript== (function () { 'use strict'; /* ---------- 工具 ---------- */ const clearAllCookies = () => { const parts = location.hostname.split('.'); const domains = [location.hostname]; if (parts.length > 1) domains.push('.' + parts.slice(-2).join('.')); document.cookie.split(';').forEach(c => { const k = c.split('=')[0].trim(); domains.forEach(d => { document.cookie = `${k}=;expires=Thu,01 Jan 1970 00:00:00 UTC;path=/;domain=${d};`; }); document.cookie = `${k}=;expires=Thu,01 Jan 1970 00:00:00 UTC;path=/;`; }); }; const getCookiesArray = () => { return document.cookie.split(';').map(c => c.trim()).filter(Boolean); }; /* ---------- 浮层宿主 ---------- */ const createTopLayerHost = (id) => { const host = document.createElement('div'); if (id) host.id = id; Object.assign(host.style, { position: 'fixed', top: '0', left: '0', width: '0', height: '0', zIndex: '2147483647', }); document.documentElement.appendChild(host); return host; }; /* ---------- Toast ---------- */ const TOAST_HOST_ID = 'cookies-inspector-toast-host'; const showToast = (msg, duration = 2000) => { let host = document.getElementById(TOAST_HOST_ID); if (!host) { host = createTopLayerHost(TOAST_HOST_ID); const shadow = host.attachShadow({ mode: 'open' }); const style = document.createElement('style'); style.textContent = ` .toast-container { position:fixed; left:0; right:0; bottom:75px; display:flex; flex-direction:column; align-items:center; gap:8px; pointer-events:none; z-index:2147483647; } .toast-item { background:rgba(28,28,30,0.85); color:#fff; padding:14px 24px; border-radius:999px; font-size:14px; font-weight:500; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; box-shadow:0 8px 24px rgba(0,0,0,0.2); animation:toastIn 0.3s cubic-bezier(0.22,1,0.36,1); opacity:0; transform:translateY(20px); animation-fill-mode:forwards; max-width:80%; text-align:center; word-break:break-word; } .toast-item.out { animation:toastOut 0.3s cubic-bezier(0.22,1,0.36,1) forwards; } @keyframes toastIn { from { opacity:0; transform:translateY(20px); } to { opacity:1; transform:translateY(0); } } @keyframes toastOut { from { opacity:1; transform:translateY(0); } to { opacity:0; transform:translateY(20px); } } `; shadow.appendChild(style); const container = document.createElement('div'); container.className = 'toast-container'; shadow.appendChild(container); } const container = host.shadowRoot.querySelector('.toast-container'); const toast = document.createElement('div'); toast.className = 'toast-item'; toast.textContent = msg; container.appendChild(toast); setTimeout(() => { toast.classList.add('out'); setTimeout(() => toast.remove(), 300); }, duration); }; /* ---------- 涟漪 ---------- */ const createRipple = function (event) { const button = event.currentTarget; button.style.transition = 'transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1)'; button.style.transform = 'scale(0.95)'; button.querySelectorAll('.ripple').forEach(r => r.remove()); const rect = button.getBoundingClientRect(); let clientX, clientY; if (event.touches) { clientX = event.touches[0].clientX; clientY = event.touches[0].clientY; } else { clientX = event.clientX; clientY = event.clientY; } const dx = Math.max(clientX - rect.left, rect.right - clientX); const dy = Math.max(clientY - rect.top, rect.bottom - clientY); const diameter = Math.sqrt(dx * dx + dy * dy) * 2; const radius = diameter / 2; const circle = document.createElement('span'); circle.classList.add('ripple'); circle.style.width = circle.style.height = diameter + 'px'; circle.style.left = (clientX - rect.left - radius) + 'px'; circle.style.top = (clientY - rect.top - radius) + 'px'; circle.style.background = 'var(--ripple-color)'; circle.style.borderRadius = '50%'; circle.style.position = 'absolute'; circle.style.pointerEvents = 'none'; circle.style.transform = 'scale(0)'; circle.style.transformOrigin = 'center center'; circle.style.opacity = '1'; circle.style.transition = 'transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)'; button.appendChild(circle); requestAnimationFrame(() => { circle.style.transform = 'scale(1)'; }); const release = () => { button.style.transform = 'scale(1)'; circle.style.transition = 'opacity 0.25s ease-out'; circle.style.opacity = '0'; setTimeout(() => { if (circle.parentElement === button) circle.remove(); }, 300); button.removeEventListener('pointerup', release); button.removeEventListener('pointerleave', release); }; button.addEventListener('pointerup', release); button.addEventListener('pointerleave', release); }; /* ---------- Cookies 弹窗 ---------- */ const showCookiesDialog = () => { if (document.getElementById('cookies-inspector-dialog')) return; const cookies = getCookiesArray(); if (cookies.length === 0) { alert('无 Cookies。'); return; } const host = createTopLayerHost('cookies-inspector-dialog'); const shadowRoot = host.attachShadow({ mode: 'open' }); const prevBodyOverflow = document.body.style.overflow; document.body.style.overflow = 'hidden'; const style = document.createElement('style'); style.textContent = ` :host { --dialog-bg: #ffffff; --dialog-shadow: 0 8px 32px rgba(0, 0, 0, 0.12); --title-color: #000000; --content-color: #000000; --fade-color: #ffffff; --scrollbar-thumb: #d1d1d1; --btn-color: #4A6FD4; --ripple-color: rgba(0, 0, 0, 0.15); } @media (prefers-color-scheme: dark) { :host { --dialog-bg: #1c1c1e; --dialog-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); --title-color: #ffffff; --content-color: #e5e5e7; --fade-color: #1c1c1e; --scrollbar-thumb: #4a4a4d; --btn-color: #6F8DE1; --ripple-color: rgba(255, 255, 255, 0.18); } } .overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0, 0, 0, 0.35); z-index: 2147483646; display: flex; justify-content: center; align-items: center; overscroll-behavior: contain; touch-action: none; } .cookies-dialog { background: var(--dialog-bg); border-radius: 20px; box-shadow: var(--dialog-shadow); z-index: 2147483647; font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; padding: 24px 24px 16px 24px; width: 80%; max-width: 300px; color: var(--content-color); position: relative; box-sizing: border-box; touch-action: auto; transition: background 0.2s ease, box-shadow 0.2s ease; } .cookies-title { font-size: 19px; font-weight: 500; margin-bottom: 24px; line-height: 1.4; color: var(--title-color); word-break: break-all; transition: color 0.2s ease; } .cookies-content-wrap { position: relative; margin-bottom: 28px; margin-right: -24px; } .cookies-content { max-height: 410px; overflow-y: auto; line-height: 1.6; word-break: break-all; white-space: pre-wrap; font-family: "SF Mono", "Menlo", "Monaco", monospace; font-size: 15px; padding: 0 4px 0 0; background: transparent; border: none; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; scrollbar-width: none; } .cookies-content::-webkit-scrollbar { display: none; } .cookies-fade { position: absolute; left: 0; right: 0; height: 24px; pointer-events: none; opacity: 0; transition: opacity 0.2s ease; } .cookies-fade.visible { opacity: 1; } .cookies-fade-top { top: 0; background: linear-gradient(to bottom, var(--fade-color) 0%, transparent 100%); } .cookies-fade-bottom { bottom: 0; background: linear-gradient(to top, var(--fade-color) 0%, transparent 100%); } .cookies-scrollbar { position: absolute; top: 0; bottom: 0; right: 0; width: 4px; pointer-events: none; opacity: 0; transition: opacity 0.3s ease; z-index: 1; } .cookies-scrollbar.visible { opacity: 1; } .cookies-scrollbar-thumb { position: absolute; left: 0; right: 0; background: var(--scrollbar-thumb); border-radius: 4px; transition: background 0.2s ease; } .cookies-buttons { display: flex; justify-content: space-between; align-items: center; gap: 12px; } .left-buttons { display: flex; margin-left: -24px; } .right-buttons { display: flex; gap: 6px; margin-right: -24px; } .cookies-btn { padding: 8px 10px; border: none; border-radius: 10px; background: transparent; cursor: pointer; font-size: 16px; font-weight: 500; transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1), color 0.2s ease; color: var(--btn-color); position: relative; overflow: hidden; -webkit-tap-highlight-color: transparent; outline: none; will-change: transform; } `; shadowRoot.appendChild(style); const overlay = document.createElement('div'); overlay.className = 'overlay'; const closeDialog = () => { document.body.style.overflow = prevBodyOverflow; host.remove(); }; overlay.onclick = (e) => { if (e.target === overlay) closeDialog(); }; const dialog = document.createElement('div'); dialog.className = 'cookies-dialog'; const title = document.createElement('div'); title.className = 'cookies-title'; title.textContent = `${location.hostname} 的 Cookies`; const contentWrap = document.createElement('div'); contentWrap.className = 'cookies-content-wrap'; const content = document.createElement('div'); content.className = 'cookies-content'; content.textContent = cookies.join(';\n'); const fadeTop = document.createElement('div'); fadeTop.className = 'cookies-fade cookies-fade-top'; const fadeBottom = document.createElement('div'); fadeBottom.className = 'cookies-fade cookies-fade-bottom'; const scrollbar = document.createElement('div'); scrollbar.className = 'cookies-scrollbar'; const scrollbarThumb = document.createElement('div'); scrollbarThumb.className = 'cookies-scrollbar-thumb'; scrollbar.appendChild(scrollbarThumb); contentWrap.appendChild(content); contentWrap.appendChild(fadeTop); contentWrap.appendChild(fadeBottom); contentWrap.appendChild(scrollbar); const updateScrollbar = () => { const visibleH = content.clientHeight; const totalH = content.scrollHeight; if (totalH <= visibleH + 1) { scrollbar.style.display = 'none'; return; } scrollbar.style.display = 'block'; const thumbH = Math.max((visibleH / totalH) * visibleH, 20); const maxThumbTop = visibleH - thumbH; const maxScrollTop = totalH - visibleH; const thumbTop = maxScrollTop > 0 ? (content.scrollTop / maxScrollTop) * maxThumbTop : 0; scrollbarThumb.style.height = thumbH + 'px'; scrollbarThumb.style.top = thumbTop + 'px'; }; const updateFades = () => { const visibleH = content.clientHeight; const totalH = content.scrollHeight; const scrollTop = content.scrollTop; if (totalH <= visibleH + 1) { fadeTop.classList.remove('visible'); fadeBottom.classList.remove('visible'); return; } const atTop = scrollTop <= 1; const atBottom = scrollTop + visibleH >= totalH - 1; fadeTop.classList.toggle('visible', !atTop); fadeBottom.classList.toggle('visible', !atBottom); }; let hideTimer = null; const showScrollbar = (duration = 800) => { scrollbar.classList.add('visible'); clearTimeout(hideTimer); hideTimer = setTimeout(() => { scrollbar.classList.remove('visible'); }, duration); }; content.addEventListener('scroll', () => { updateScrollbar(); updateFades(); showScrollbar(800); }); requestAnimationFrame(() => { updateScrollbar(); updateFades(); if (scrollbar.style.display !== 'none') showScrollbar(1200); }); const buttonsDiv = document.createElement('div'); buttonsDiv.className = 'cookies-buttons'; const leftButtons = document.createElement('div'); leftButtons.className = 'left-buttons'; const rightButtons = document.createElement('div'); rightButtons.className = 'right-buttons'; const deleteBtn = document.createElement('button'); deleteBtn.className = 'cookies-btn delete'; deleteBtn.textContent = '删除'; deleteBtn.onclick = () => { if (confirm('确定要删除全部 Cookies 吗?')) { clearAllCookies(); showToast('Cookies 已删除'); closeDialog(); } }; deleteBtn.addEventListener('pointerdown', createRipple); const copyBtn = document.createElement('button'); copyBtn.className = 'cookies-btn'; copyBtn.textContent = '复制'; copyBtn.onclick = () => { GM_setClipboard(getCookiesArray().join('; ')); showToast('已复制文本'); closeDialog(); }; copyBtn.addEventListener('pointerdown', createRipple); const cancelBtn = document.createElement('button'); cancelBtn.className = 'cookies-btn'; cancelBtn.textContent = '取消'; cancelBtn.onclick = () => closeDialog(); cancelBtn.addEventListener('pointerdown', createRipple); leftButtons.appendChild(deleteBtn); rightButtons.appendChild(cancelBtn); rightButtons.appendChild(copyBtn); buttonsDiv.appendChild(leftButtons); buttonsDiv.appendChild(rightButtons); dialog.append(title, contentWrap, buttonsDiv); overlay.appendChild(dialog); shadowRoot.appendChild(overlay); const intervalId = setInterval(() => { const currentCookies = getCookiesArray().join(';\n'); if (content.textContent !== currentCookies) { content.textContent = currentCookies; updateScrollbar(); updateFades(); if (getCookiesArray().length === 0) { clearInterval(intervalId); closeDialog(); showToast('Cookies 已清空'); } } }, 1000); const observer = new MutationObserver(() => { if (!document.contains(host)) { clearInterval(intervalId); observer.disconnect(); clearTimeout(hideTimer); document.body.style.overflow = prevBodyOverflow; } }); observer.observe(document.documentElement, { childList: true, subtree: true }); }; /* ---------- 菜单 ---------- */ GM_registerMenuCommand('查看 Cookies', () => { const cookies = getCookiesArray(); if (cookies.length === 0) { alert('无 Cookies。'); } else { showCookiesDialog(); } }); })();