Text Saver

Saves text for easy access.

  1. // ==UserScript==
  2. // @name Text Saver
  3. // @namespace http://tampermonkey.net/
  4. // @version 3.2
  5. // @description Saves text for easy access.
  6. // @author You
  7. // @match *://*/*
  8. // @grant GM_addStyle
  9. // @grant GM_setClipboard
  10. // @grant GM_notification
  11. // @grant GM_setValue
  12. // @grant GM_getValue
  13. // @license MIT
  14. // ==/UserScript==
  15.  
  16. (function() {
  17. 'use strict';
  18.  
  19. let savedTexts = GM_getValue('savedTexts', []);
  20. let isModalVisible = false;
  21. let dragOffset = { x: 0, y: 0 };
  22. let lastCopiedIndex = -1;
  23. let editingIndex = -1;
  24.  
  25. if (savedTexts.length > 0) {
  26. const shouldRestore = confirm('Restore previous session?');
  27. if (!shouldRestore) {
  28. savedTexts = [];
  29. GM_setValue('savedTexts', []);
  30. }
  31. }
  32.  
  33. const button = document.createElement('button');
  34. button.textContent = '<';
  35. button.style.position = 'fixed';
  36. button.style.top = '50%';
  37. button.style.right = '10px';
  38. button.style.transform = 'translateY(-50%)';
  39. button.style.zIndex = '1000';
  40. button.style.backgroundColor = 'black';
  41. button.style.color = 'white';
  42. button.style.padding = '10px 15px';
  43. button.style.border = 'none';
  44. button.style.borderRadius = '5px';
  45. button.style.cursor = 'pointer';
  46. button.style.fontSize = '16px';
  47. document.body.appendChild(button);
  48.  
  49. const modal = document.createElement('div');
  50. modal.style.position = 'fixed';
  51. modal.style.top = '100px';
  52. modal.style.right = '50px';
  53. modal.style.width = '200px';
  54. modal.style.backgroundColor = 'white';
  55. modal.style.zIndex = '1001';
  56. modal.style.display = 'none';
  57. modal.style.border = '1px solid #ccc';
  58. modal.style.borderRadius = '5px';
  59. modal.style.padding = '10px';
  60. modal.style.transition = 'transform 0.3s ease-out';
  61. document.body.appendChild(modal);
  62.  
  63. const modalHeader = document.createElement('div');
  64. modalHeader.style.backgroundColor = '#ddd';
  65. modalHeader.style.color = 'black';
  66. modalHeader.style.padding = '5px';
  67. modalHeader.style.cursor = 'move';
  68. modal.appendChild(modalHeader);
  69.  
  70. modalHeader.addEventListener('mousedown', (e) => {
  71. let isDragging = true;
  72. let dragOffset = { x: 0, y: 0 };
  73.  
  74. dragOffset.x = e.clientX - modal.offsetLeft;
  75. dragOffset.y = e.clientY - modal.offsetTop;
  76.  
  77. document.addEventListener('mousemove', (e) => {
  78. if (!isDragging) return;
  79.  
  80. modal.style.right = 'auto';
  81. modal.style.left = (e.clientX - dragOffset.x) + 'px';
  82. modal.style.top = (e.clientY - dragOffset.y) + 'px';
  83. });
  84.  
  85. document.addEventListener('mouseup', () => {
  86. isDragging = false;
  87. });
  88. document.addEventListener('mouseleave', () => {
  89. isDragging = false;
  90. });
  91. });
  92.  
  93. const textContainer = document.createElement('div');
  94. textContainer.style.maxHeight = '300px';
  95. textContainer.style.overflowY = 'auto';
  96. modal.appendChild(modalHeader);
  97. modal.appendChild(textContainer);
  98.  
  99. GM_addStyle(`
  100. .saved-text-item {
  101. cursor: pointer;
  102. margin-bottom: 5px;
  103. padding: 5px;
  104. border: 1px solid #ccc;
  105. border-radius: 3px;
  106. font-size: 14px;
  107. word-break: break-word;
  108. }
  109.  
  110. .saved-text-item:hover {
  111. background-color: #f0f0f0;
  112. }
  113.  
  114. .selected-text-item {
  115. background-color: #add8e6;
  116. }
  117.  
  118. @keyframes pulse {
  119. 0% {
  120. transform: scale(1);
  121. opacity: 0.5;
  122. }
  123. 50% {
  124. transform: scale(1.1);
  125. opacity: 1;
  126. }
  127. 100% {
  128. transform: scale(1);
  129. opacity: 0.5;
  130. }
  131. }
  132.  
  133. .modal-enter {
  134. animation: pulse 0.5s ease-in-out;
  135. }
  136.  
  137. .modal-exit {
  138. transform: scale(0.5);
  139. opacity: 0;
  140. }
  141.  
  142. .editing-text-item {
  143. background-color: yellow;
  144. }
  145. `);
  146.  
  147. function updateTextContainer() {
  148. textContainer.innerHTML = '';
  149.  
  150. savedTexts.forEach((text, index) => {
  151. const textItem = document.createElement('div');
  152. textItem.classList.add('saved-text-item');
  153. textItem.textContent = text;
  154.  
  155. if (index === lastCopiedIndex) {
  156. textItem.classList.add('selected-text-item');
  157. }
  158.  
  159. if (index === editingIndex) {
  160. textItem.classList.add('editing-text-item');
  161. }
  162.  
  163. textItem.addEventListener('click', (event) => {
  164. if (event.detail === 2) {
  165. startEditing(index);
  166. } else {
  167. copyText(index);
  168. }
  169. });
  170.  
  171. textContainer.appendChild(textItem);
  172. });
  173. }
  174.  
  175. function showMessage(message, type = 'success') {
  176. GM_notification({
  177. title: 'Text Saver',
  178. text: message,
  179. timeout: 3000
  180. });
  181. }
  182.  
  183. function copyText(index) {
  184. if (index >= 0 && index < savedTexts.length) {
  185. const text = savedTexts[index];
  186. GM_setClipboard(text);
  187. lastCopiedIndex = index;
  188. updateTextContainer();
  189. showMessage(`Text copied: ${text.substring(0, 50)}${text.length > 50 ? '...' : ''}`);
  190. }
  191. }
  192.  
  193. function deleteText(index) {
  194. if (index >= 0 && index < savedTexts.length) {
  195. const deletedText = savedTexts[index];
  196. savedTexts.splice(index, 1);
  197.  
  198. if (lastCopiedIndex === index) {
  199. lastCopiedIndex = -1;
  200. } else if (lastCopiedIndex > index) {
  201. lastCopiedIndex--;
  202. }
  203.  
  204. if (editingIndex === index) {
  205. editingIndex = -1;
  206. } else if (editingIndex > index) {
  207. editingIndex--;
  208. }
  209. updateTextContainer();
  210. showMessage(`Text deleted: ${deletedText.substring(0, 50)}${deletedText.length > 50 ? '...' : ''}`, 'error');
  211. }
  212. }
  213.  
  214. function startEditing(index) {
  215. if (index >= 0 && index < savedTexts.length) {
  216. editingIndex = index;
  217. updateTextContainer();
  218.  
  219. const textItem = textContainer.children[index];
  220. if (textItem) {
  221. textItem.contentEditable = 'true';
  222. textItem.focus();
  223.  
  224. textItem.addEventListener('blur', () => {
  225. stopEditing(index, textItem.textContent);
  226. }, { once: true });
  227. }
  228. }
  229. }
  230.  
  231. function stopEditing(index, newText) {
  232. if (index >= 0 && index < savedTexts.length) {
  233. savedTexts[index] = newText;
  234. editingIndex = -1;
  235. updateTextContainer();
  236. showMessage(`Text changed: ${newText.substring(0, 50)}${newText.length > 50 ? '...' : ''}`);
  237. }
  238. }
  239.  
  240. function deleteAllTexts() {
  241. savedTexts = [];
  242. lastCopiedIndex = -1;
  243. editingIndex = -1;
  244. updateTextContainer();
  245. showMessage('All texts deleted!', 'error');
  246. }
  247.  
  248. function addTextToClipboard(text) {
  249. const trimmedText = text.trim();
  250. if (trimmedText !== "") {
  251. savedTexts.push(trimmedText);
  252. lastCopiedIndex = savedTexts.length - 1;
  253. updateTextContainer();
  254. GM_setValue('savedTexts', savedTexts);
  255. }
  256. }
  257.  
  258. document.addEventListener('copy', (event) => {
  259. const selectedText = window.getSelection().toString();
  260. if (selectedText) {
  261. addTextToClipboard(selectedText);
  262. }
  263. });
  264.  
  265. button.addEventListener('click', () => {
  266. if (!isModalVisible) {
  267. modal.classList.remove('modal-exit');
  268. modal.classList.add('modal-enter');
  269. modal.style.display = 'block';
  270. } else {
  271. modal.classList.remove('modal-enter');
  272. modal.classList.add('modal-exit');
  273. setTimeout(() => {
  274. modal.style.display = 'none';
  275. modal.classList.remove('modal-exit');
  276. }, 300);
  277. }
  278. isModalVisible = !isModalVisible;
  279. updateTextContainer();
  280. });
  281.  
  282. updateTextContainer();
  283.  
  284. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址