Git Image Preview (Ctrl/Option Click)

按住 Ctrl/Option 点击 GitHub 页面中的图片,即可在当前页面预览原图,支持滚轮缩放

  1. // ==UserScript==
  2. // @name Git Image Preview (Ctrl/Option Click)
  3. // @namespace https://github.com/stuarthua/git-image-preview
  4. // @version 1.0.2
  5. // @description 按住 Ctrl/Option 点击 GitHub 页面中的图片,即可在当前页面预览原图,支持滚轮缩放
  6. // @author stuarthua
  7. // @match https://github.com/*
  8. // @icon https://raw.githubusercontent.com/stuarthua/git-image-preview/main/images/icon.png
  9. // @homepageURL https://github.com/stuarthua/git-image-preview
  10. // @license MIT
  11. // @grant GM_addStyle
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. // Add custom CSS for the modal
  18. GM_addStyle(`
  19. .image-preview-overlay {
  20. position: fixed;
  21. top: 0;
  22. left: 0;
  23. width: 100%;
  24. height: 100%;
  25. background: rgba(0, 0, 0, 0.8);
  26. display: flex;
  27. justify-content: center;
  28. align-items: center;
  29. z-index: 9999;
  30. }
  31. .image-preview-overlay img {
  32. max-width: 90%;
  33. max-height: 90%;
  34. box-shadow: 0 0 20px black;
  35. transition: transform 0.2s ease;
  36. }
  37. .image-preview-overlay .close-button {
  38. position: absolute;
  39. top: 20px;
  40. right: 20px;
  41. color: white;
  42. font-size: 30px;
  43. cursor: pointer;
  44. }
  45. `);
  46.  
  47. function showImagePreview(src) {
  48. const overlay = document.createElement('div');
  49. overlay.className = 'image-preview-overlay';
  50.  
  51. const img = document.createElement('img');
  52. img.src = src;
  53. let scale = 1;
  54.  
  55. overlay.addEventListener('wheel', (e) => {
  56. e.preventDefault();
  57. scale += e.deltaY < 0 ? 0.1 : -0.1;
  58. scale = Math.min(Math.max(scale, 0.5), 5);
  59. img.style.transform = `scale(${scale})`;
  60. });
  61.  
  62. const closeButton = document.createElement('span');
  63. closeButton.className = 'close-button';
  64. closeButton.textContent = '×';
  65. closeButton.onclick = () => document.body.removeChild(overlay);
  66.  
  67. overlay.appendChild(img);
  68. overlay.appendChild(closeButton);
  69. overlay.onclick = (e) => {
  70. if (e.target === overlay) {
  71. document.body.removeChild(overlay);
  72. }
  73. };
  74.  
  75. document.body.appendChild(overlay);
  76. }
  77.  
  78. // Listen in capture phase to intercept before default behavior
  79. document.addEventListener('click', function (e) {
  80. const link = e.target.closest('a');
  81. if (!link || !link.querySelector('img')) return;
  82.  
  83. const img = link.querySelector('img');
  84. const isInMarkdown = link.closest('.markdown-body');
  85. const isCtrlOrOptionPressed = e.ctrlKey || e.altKey || e.metaKey;
  86.  
  87. if (img && isInMarkdown && isCtrlOrOptionPressed) {
  88. e.preventDefault();
  89. e.stopImmediatePropagation();
  90. showImagePreview(img.src);
  91. }
  92. }, true);
  93. })();