Add Copy Button to Chat Messages on Gtihub Copilot web page

Adds a "Copy" button to chat message elements to easily copy their content.

目前为 2024-12-10 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Add Copy Button to Chat Messages on Gtihub Copilot web page
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Adds a "Copy" button to chat message elements to easily copy their content.
  6. // @author aspen138
  7. // @match *://github.com/copilot/c/*
  8. // @match *://github.com/copilot/
  9. // @match *://github.com/copilot/*
  10. // @grant none
  11. // @run-at document-end
  12. // @icon https://github.com/favicons/favicon-copilot.svg
  13. // @license MIT
  14. // ==/UserScript==
  15.  
  16. (function() {
  17. 'use strict';
  18.  
  19. // Configuration: Update these class names if they change
  20. const MESSAGE_CONTENT_CLASS = 'UserMessage-module__container--cAvvK';
  21. const CHAT_MESSAGE_CONTENT_CLASS = 'ChatMessage-module__content--MYneF';
  22.  
  23. /**
  24. * Creates and returns a copy button element.
  25. * @returns {HTMLButtonElement} The copy button.
  26. */
  27. function createCopyButton() {
  28. const button = document.createElement('button');
  29. button.innerText = 'Copy';
  30. button.style.marginLeft = '10px';
  31. button.style.padding = '5px 10px';
  32. button.style.cursor = 'pointer';
  33. button.style.fontSize = '0.9em';
  34. // Optional: Add more styles to match your site's design
  35. return button;
  36. }
  37.  
  38. /**
  39. * Adds a copy button to a chat message element.
  40. * @param {HTMLElement} messageElement The chat message element.
  41. */
  42. function addCopyButton(messageElement) {
  43. // Prevent adding multiple buttons to the same message
  44. if (messageElement.querySelector('.copy-button')) {
  45. return;
  46. }
  47.  
  48. const messageContent = messageElement.querySelector(`.${MESSAGE_CONTENT_CLASS}`);
  49. if (!messageContent) return;
  50.  
  51. const copyButton = createCopyButton();
  52. copyButton.classList.add('copy-button');
  53.  
  54. // Event listener for the copy action
  55. copyButton.addEventListener('click', () => {
  56. const textToCopy = messageContent.innerText.trim();
  57. navigator.clipboard.writeText(textToCopy).then(() => {
  58. // Optional: Provide feedback to the user
  59. copyButton.innerText = 'Copied!';
  60. setTimeout(() => {
  61. copyButton.innerText = 'Copy';
  62. }, 2000);
  63. }).catch(err => {
  64. console.error('Failed to copy text: ', err);
  65. });
  66. });
  67.  
  68. // Append the copy button to the message element
  69. // Adjust the append location as needed
  70. messageElement.appendChild(copyButton);
  71. }
  72.  
  73. /**
  74. * Processes all existing chat message elements on the page.
  75. */
  76. function processExistingMessages() {
  77. const messageElements = document.querySelectorAll(`.${CHAT_MESSAGE_CONTENT_CLASS}`);
  78. messageElements.forEach(messageElement => {
  79. addCopyButton(messageElement);
  80. });
  81. }
  82.  
  83. /**
  84. * Sets up a MutationObserver to watch for new chat messages being added to the DOM.
  85. */
  86. function observeNewMessages() {
  87. const targetNode = document.body;
  88. const config = { childList: true, subtree: true };
  89.  
  90. const callback = function(mutationsList) {
  91. for (const mutation of mutationsList) {
  92. if (mutation.type === 'childList') {
  93. mutation.addedNodes.forEach(node => {
  94. if (node.nodeType === Node.ELEMENT_NODE) {
  95. // Check if the added node is a chat message
  96. if (node.classList && node.classList.contains(CHAT_MESSAGE_CONTENT_CLASS)) {
  97. addCopyButton(node);
  98. }
  99.  
  100. // Also check within the subtree of the added node
  101. const nestedMessages = node.querySelectorAll(`.${CHAT_MESSAGE_CONTENT_CLASS}`);
  102. nestedMessages.forEach(nestedNode => {
  103. addCopyButton(nestedNode);
  104. });
  105. }
  106. });
  107. }
  108. }
  109. };
  110.  
  111. const observer = new MutationObserver(callback);
  112. observer.observe(targetNode, config);
  113. }
  114.  
  115. /**
  116. * Initializes the script by processing existing messages and setting up observers.
  117. */
  118. function init() {
  119. processExistingMessages();
  120. observeNewMessages();
  121. }
  122.  
  123. // Wait for the DOM to be fully loaded before initializing
  124. if (document.readyState === 'loading') {
  125. document.addEventListener('DOMContentLoaded', init);
  126. } else {
  127. init();
  128. }
  129.  
  130. })();