Greasy Fork is available in English.

LeetCode Draw

在 leetcode.cn 上添加绘图功能,右侧显示 Excalidraw 画板

  1. // ==UserScript==
  2. // @name LeetCode Draw
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.3
  5. // @description 在 leetcode.cn 上添加绘图功能,右侧显示 Excalidraw 画板
  6. // @license MIT
  7. // @author mxgxxx
  8. // @match https://leetcode.cn/*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (() => {
  13. 'use strict';
  14.  
  15. const storage = {
  16. save: (width, isVisible) => {
  17. localStorage.setItem('leetcode-draw-width', width);
  18. localStorage.setItem('leetcode-draw-visible', isVisible);
  19. },
  20. load: () => ({
  21. width: localStorage.getItem('leetcode-draw-width') || '50%',
  22. isVisible: localStorage.getItem('leetcode-draw-visible') === 'true'
  23. })
  24. };
  25.  
  26. const savedState = storage.load();
  27. const container = document.createElement('div');
  28. container.style.position = 'fixed';
  29. container.style.top = '0';
  30. container.style.right = '0';
  31. container.style.width = savedState.width;
  32. container.style.height = '100%';
  33. container.style.display = savedState.isVisible ? 'block' : 'none';
  34. container.style.zIndex = '9999';
  35. container.style.borderLeft = '1px solid #ccc';
  36.  
  37. const resizer = document.createElement('div');
  38. resizer.style.width = '10px';
  39. resizer.style.height = '100%';
  40. resizer.style.position = 'absolute';
  41. resizer.style.left = '-5px';
  42. resizer.style.top = '0';
  43. resizer.style.cursor = 'ew-resize';
  44. resizer.style.backgroundColor = '#ccc';
  45. container.appendChild(resizer);
  46.  
  47. const iframe = document.createElement('iframe');
  48. iframe.src = 'https://excalidraw.com';
  49. iframe.style.width = '100%';
  50. iframe.style.height = '100%';
  51. container.appendChild(iframe);
  52.  
  53. let isDragging = false;
  54. let currentX;
  55. let initialWidth;
  56.  
  57. function initDrag(e) {
  58. if (e.button !== 0) return;
  59. isDragging = true;
  60. currentX = e.clientX;
  61. initialWidth = container.getBoundingClientRect().width;
  62. document.body.style.userSelect = 'none';
  63. document.addEventListener('mousemove', handleDrag);
  64. document.addEventListener('mouseup', stopDrag);
  65. if (iframe) iframe.style.pointerEvents = 'none';
  66. }
  67.  
  68. function handleDrag(e) {
  69. if (!isDragging) return;
  70. const deltaX = currentX - e.clientX;
  71. const newWidth = initialWidth + deltaX;
  72. if (newWidth >= 200 && newWidth <= (window.innerWidth - 100)) {
  73. container.style.width = `${newWidth}px`;
  74. storage.save(container.style.width, container.style.display === 'block');
  75. }
  76. }
  77.  
  78. function stopDrag() {
  79. isDragging = false;
  80. document.body.style.userSelect = '';
  81. if (iframe) iframe.style.pointerEvents = 'auto';
  82. document.removeEventListener('mousemove', handleDrag);
  83. document.removeEventListener('mouseup', stopDrag);
  84. }
  85.  
  86. document.addEventListener('mousedown', (e) => {
  87. const resizerRect = resizer.getBoundingClientRect();
  88. if (e.clientX >= resizerRect.left && e.clientX <= resizerRect.right) {
  89. initDrag(e);
  90. }
  91. });
  92.  
  93. const toggleButton = document.createElement('button');
  94. const iconSpan = document.createElement('span');
  95. iconSpan.textContent = '🖌';
  96. toggleButton.innerHTML = '';
  97. toggleButton.appendChild(iconSpan);
  98.  
  99. toggleButton.style.position = 'fixed';
  100. toggleButton.style.right = '0';
  101. toggleButton.style.top = '50%';
  102. toggleButton.style.transform = 'translateY(-50%)';
  103. toggleButton.style.padding = '8px';
  104. toggleButton.style.borderRadius = '8px 0 0 8px';
  105. toggleButton.style.backgroundColor = '#ffa116';
  106. toggleButton.style.color = '#fff';
  107. toggleButton.style.cursor = 'pointer';
  108. toggleButton.style.opacity = '0.7';
  109. toggleButton.style.width = '40px';
  110. toggleButton.style.transition = 'opacity 0.3s';
  111.  
  112. toggleButton.addEventListener('mouseover', () => {
  113. toggleButton.style.opacity = '1';
  114. });
  115. toggleButton.addEventListener('mouseout', () => {
  116. toggleButton.style.opacity = '0.7';
  117. });
  118.  
  119. toggleButton.style.zIndex = '10000';
  120.  
  121. toggleButton.addEventListener('click', () => {
  122. const newDisplay = container.style.display === 'none' ? 'block' : 'none';
  123. container.style.display = newDisplay;
  124. storage.save(container.style.width, newDisplay === 'block');
  125. });
  126.  
  127. document.body.appendChild(toggleButton);
  128. document.body.appendChild(container);
  129. })();