Add "Copy" Button to ChatGPT Questions

Add copy buttons to your prompts on ChatGPT, in case you need to ask the same question twice

当前为 2024-04-07 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Add "Copy" Button to ChatGPT Questions
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description Add copy buttons to your prompts on ChatGPT, in case you need to ask the same question twice
  6. // @author Lak
  7. // @match chat.openai.com/*
  8. // @license MIT
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // Function to copy text to clipboard
  16. function copyTextToClipboard(text) {
  17. const textArea = document.createElement('textarea');
  18. textArea.value = text;
  19. document.body.appendChild(textArea);
  20. textArea.select();
  21. document.execCommand('copy');
  22. document.body.removeChild(textArea);
  23. }
  24.  
  25. // Function to add a copy button to an element
  26. function addCopyButton(element) {
  27. const existingCopyButton = element.querySelector('[aria-label="Copier"]');
  28. if (!existingCopyButton) {
  29. const copyButton = document.createElement('button');
  30. copyButton.innerHTML = '<svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg> ';
  31. copyButton.className = "flex items-center gap-1.5 rounded-md p-1 text-xs hover:text-gray-950 dark:text-gray-400 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400 md:invisible md:group-hover:visible md:group-[.final-completion]:visible";
  32. copyButton.ariaLabel = 'Copier';
  33. const buttonPosition = element.querySelector('.mt-1');
  34. if (buttonPosition) {
  35. const firstChild = buttonPosition.firstElementChild;
  36. if (firstChild) {
  37. buttonPosition.insertBefore(copyButton, firstChild);
  38. } else {
  39. buttonPosition.appendChild(copyButton);
  40. }
  41. }
  42.  
  43. copyButton.addEventListener('click', () => {
  44. const innerText = element.querySelector('[class*="text-message"]').innerText.trim();
  45. copyTextToClipboard(innerText);
  46.  
  47. // Change the copy button icon to a checkmark temporarily
  48. const originalIcon = copyButton.innerHTML;
  49. copyButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="h-4 w-4"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>';
  50.  
  51. setTimeout(() => {
  52. // Revert the copy button icon to the original
  53. copyButton.innerHTML = originalIcon;
  54. }, 1500); // 1.5 seconds delay for reverting the button icon
  55. });
  56. }
  57. }
  58.  
  59. // Observer callback function
  60. function observeCallback(mutationsList, observer) {
  61. for (const mutation of mutationsList) {
  62. if (mutation.addedNodes) {
  63. for (const node of mutation.addedNodes) {
  64. if (node instanceof Element) {
  65. const elements = document.querySelectorAll('[data-testid*="conversation-turn-"]');
  66. for (let i = 0; i < elements.length; i += 2) {
  67. addCopyButton(elements[i]);
  68. }
  69. }
  70. }
  71. }
  72. }
  73. }
  74.  
  75.  
  76.  
  77. // Create a Mutation Observer
  78. const observer = new MutationObserver(observeCallback);
  79.  
  80. // Start observing the entire document body for changes
  81. observer.observe(document.body, { childList: true, subtree: true });
  82. })();