您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a styled context menu option to open Instagram profile pictures in new tab
// ==UserScript== // @name Instagram Profile Picture Opener // @namespace https://github.com/GooglyBlox // @version 1.3 // @description Adds a styled context menu option to open Instagram profile pictures in new tab // @author GooglyBlox // @match https://www.instagram.com/* // @match https://www.instagram.com/ // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant unsafeWindow // @run-at document-start // @inject-into content // @license MIT // ==/UserScript== (function() { 'use strict'; GM_addStyle(` .ig-custom-context-menu { position: fixed; z-index: 999999; background: rgba(255, 255, 255, 0.98); backdrop-filter: blur(12px); border: 1px solid rgba(219, 219, 219, 0.2); border-radius: 12px; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15); padding: 8px; min-width: 240px; animation: menuFadeIn 0.2s ease-out; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; } .ig-custom-context-menu-item { display: flex; align-items: center; padding: 12px 16px; color: #262626; font-size: 14px; font-weight: 500; cursor: pointer; border-radius: 8px; transition: all 0.2s ease; gap: 12px; } .ig-custom-context-menu-item:hover { background-color: rgba(0, 0, 0, 0.05); } .ig-custom-context-menu-item svg { width: 20px; height: 20px; flex-shrink: 0; } @keyframes menuFadeIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } } .ig-custom-divider { height: 1px; background: rgba(219, 219, 219, 0.8); margin: 8px 4px; } @media (prefers-color-scheme: dark) { .ig-custom-context-menu { background: rgba(38, 38, 38, 0.98); border-color: rgba(38, 38, 38, 0.2); } .ig-custom-context-menu-item { color: #fafafa; } .ig-custom-context-menu-item:hover { background-color: rgba(255, 255, 255, 0.1); } .ig-custom-divider { background: rgba(38, 38, 38, 0.8); } } `); function getHDProfilePicUrl(url) { return url; } const icons = { openInNew: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> </svg>`, download: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /> </svg>` }; function handleContextMenu(event) { const img = event.target; if (img.tagName === 'IMG' && ( img.closest('a[href^="/"]') !== null || img.closest('[role="button"]') !== null || img.closest('article') !== null )) { event.stopPropagation(); event.preventDefault(); const existingMenu = document.querySelector('.ig-custom-context-menu'); if (existingMenu) { document.body.removeChild(existingMenu); } const contextMenu = document.createElement('div'); contextMenu.className = 'ig-custom-context-menu'; const rect = event.target.getBoundingClientRect(); const x = event.clientX; const y = event.clientY; const menuWidth = 240; const menuHeight = 120; const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; const adjustedX = Math.min(x, viewportWidth - menuWidth - 10); const adjustedY = Math.min(y, viewportHeight - menuHeight - 10); contextMenu.style.left = `${adjustedX}px`; contextMenu.style.top = `${adjustedY}px`; const openInNewTab = document.createElement('div'); openInNewTab.className = 'ig-custom-context-menu-item'; openInNewTab.innerHTML = `${icons.openInNew}Open Profile Picture`; openInNewTab.addEventListener('click', () => { const hdUrl = getHDProfilePicUrl(img.src); window.open(hdUrl, '_blank'); document.body.removeChild(contextMenu); }); const downloadImage = document.createElement('div'); downloadImage.className = 'ig-custom-context-menu-item'; downloadImage.innerHTML = `${icons.download}Download Profile Picture`; downloadImage.addEventListener('click', async () => { const hdUrl = getHDProfilePicUrl(img.src); const username = img.closest('a')?.getAttribute('href')?.replace(/\//g, '') || 'profile'; try { const response = await fetch(hdUrl); const blob = await response.blob(); const blobUrl = window.URL.createObjectURL(blob); const downloadLink = document.createElement('a'); downloadLink.href = blobUrl; downloadLink.download = `${username}_profile_picture.jpg`; downloadLink.click(); window.URL.revokeObjectURL(blobUrl); } catch (error) { console.error('Failed to download image:', error); } document.body.removeChild(contextMenu); }); contextMenu.appendChild(openInNewTab); contextMenu.appendChild(document.createElement('div')).className = 'ig-custom-divider'; contextMenu.appendChild(downloadImage); document.body.appendChild(contextMenu); function removeContextMenu(e) { if (!contextMenu.contains(e.target)) { document.body.removeChild(contextMenu); document.removeEventListener('click', removeContextMenu); document.removeEventListener('contextmenu', removeContextMenu); } } document.addEventListener('click', removeContextMenu); document.addEventListener('contextmenu', removeContextMenu); } } function init() { document.addEventListener('contextmenu', handleContextMenu, true); } const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.addedNodes.length) { init(); } }); }); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { init(); observer.observe(document.body, { childList: true, subtree: true }); }); } else { init(); observer.observe(document.body, { childList: true, subtree: true }); } })();