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