Guest User

Untitled

a guest
Jul 27th, 2025
6
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.87 KB | None | 0 0
  1. // ==UserScript==
  2. // @name Character Prompt Copy
  3. // @namespace http://tampermonkey.net/
  4. // @version 4.0
  5. // @description Finds <character> tags via regex to copy content, ignoring outside text. Includes append-to-buffer functionality.
  6. // @author Fey
  7. // @match *://2ch.hk/wr/*
  8. // @match *://2ch.life/wr/*
  9. // @grant GM_setClipboard
  10. // @grant GM_addStyle
  11. // @grant GM_setValue
  12. // @grant GM_getValue
  13. // ==/UserScript==
  14. (function() {
  15. 'use strict';
  16. // --- 1. Define SVG Icons ---
  17. const copyIconSVG = `<svg xmlns="http://www.w3.org/2000/svg" height="16" viewBox="0 0 24 24" width="16" fill="currentColor"><path d="M0 0h24v24H0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>`;
  18. const addIconSVG = `<svg xmlns="http://www.w3.org/2000/svg" height="16" viewBox="0 0 24 24" width="16" fill="currentColor"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>`;
  19. const checkIconSVG = `<svg xmlns="http://www.w3.org/2000/svg" height="16" viewBox="0 0 24 24" width="16" fill="currentColor"><path d="M0 0h24v24H0z" fill="none"/><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`;
  20. // --- 2. Define Local Storage Key & Regex ---
  21. const STORAGE_KEY = 'tampermonkey_post_compiler_buffer';
  22. // This regex captures <character ...> ... </character> blocks.
  23. // It's non-greedy (s*?) to handle multiple blocks in one post correctly.
  24. const CHARACTER_REGEX = /<character[^>]*>[\s\S]*?<\/character>/g;
  25. // --- 3. Style the buttons ---
  26. GM_addStyle(`
  27. .post-copy-btn { background: none; border: none; padding: 0 0 0 5px; margin: 0; cursor: pointer; color: #d38d8d; display: inline-flex; align-items: center; vertical-align: middle; transition: all 0.2s ease; }
  28. .post-copy-btn:hover { color: #f00; transform: scale(1.1); }
  29. .post-copy-btn.copied { color: #78b346; cursor: default; }
  30. .post-copy-btn.copied:hover { transform: none; }
  31. `);
  32. // --- 4. Core Logic ---
  33. const addCopyButtons = (postNode) => {
  34. if (!postNode || typeof postNode.querySelector !== 'function') return;
  35. const targetContainer = postNode.querySelector('.post__details');
  36. if (!targetContainer || targetContainer.querySelector('.post-copy-btn')) return;
  37. // A helper function to extract text using the new Regex method.
  38. const getTextToCopy = (messageElement) => {
  39. if (!messageElement) return '';
  40. // Get the raw HTML content of the post as a string.
  41. let postHtml = messageElement.innerText;
  42. // Use the regex to find all <character> blocks.
  43. const matches = postHtml.match(CHARACTER_REGEX);
  44. if (matches && matches.length > 0) {
  45. // If found, join the matched blocks. This implicitly ignores all other text.
  46. // Replace <br> tags with newlines for proper formatting in clipboard.
  47. return matches.join('\n\n').replace(/<br\s*\/?>/gi, '\n');
  48. } else {
  49. // If no character tags are found, fall back to copying the visible text.
  50. return messageElement.innerText.trim();
  51. }
  52. };
  53. // A helper for showing feedback on a button
  54. const showFeedback = (button, originalIcon, originalTitle) => {
  55. button.innerHTML = checkIconSVG;
  56. button.classList.add('copied');
  57. setTimeout(() => {
  58. button.innerHTML = originalIcon;
  59. button.classList.remove('copied');
  60. button.title = originalTitle;
  61. }, 1500);
  62. };
  63. // --- Create Button 1: Copy/Replace ---
  64. const copyButton = document.createElement('button');
  65. copyButton.className = 'post-copy-btn copy-replace-btn';
  66. copyButton.innerHTML = copyIconSVG;
  67. copyButton.title = 'Copy (Replace)';
  68. copyButton.addEventListener('click', (event) => {
  69. event.preventDefault();
  70. const messageElement = postNode.querySelector('.post__message');
  71. const postText = getTextToCopy(messageElement);
  72. if (postText) {
  73. GM_setClipboard(postText);
  74. GM_setValue(STORAGE_KEY, postText); // Also resets the buffer
  75. copyButton.title = 'Copied! (Buffer reset)';
  76. showFeedback(copyButton, copyIconSVG, 'Copy (Replace)');
  77. }
  78. });
  79. // --- Create Button 2: Add/Append ---
  80. const addButton = document.createElement('button');
  81. addButton.className = 'post-copy-btn copy-append-btn';
  82. addButton.innerHTML = addIconSVG;
  83. const updateAddButtonTitle = () => {
  84. const buffer = GM_getValue(STORAGE_KEY, '');
  85. const count = buffer ? buffer.split('\n\n---\n\n').length : 0;
  86. addButton.title = `Append (Buffer: ${count} item${count === 1 ? '' : 's'})`;
  87. };
  88. updateAddButtonTitle(); // Set initial title
  89. addButton.addEventListener('click', (event) => {
  90. event.preventDefault();
  91. const messageElement = postNode.querySelector('.post__message');
  92. const postText = getTextToCopy(messageElement);
  93. if (postText) {
  94. let currentBuffer = GM_getValue(STORAGE_KEY, '');
  95. let newBuffer = currentBuffer ? (currentBuffer + '\n\n---\n\n' + postText) : postText;
  96. GM_setValue(STORAGE_KEY, newBuffer);
  97. GM_setClipboard(newBuffer);
  98. updateAddButtonTitle();
  99. addButton.title = 'Appended & Copied!';
  100. showFeedback(addButton, addIconSVG, addButton.title); // Pass updated title back
  101. }
  102. });
  103. targetContainer.appendChild(copyButton);
  104. targetContainer.appendChild(addButton);
  105. };
  106. // --- 5. Execution Logic ---
  107. const processAllPosts = () => document.querySelectorAll('.post.post_type_reply').forEach(addCopyButtons);
  108. const observer = new MutationObserver((mutationsList) => {
  109. for (const mutation of mutationsList) {
  110. if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
  111. mutation.addedNodes.forEach(node => {
  112. if (node.nodeType === 1) {
  113. if (node.matches('.post.post_type_reply')) {
  114. addCopyButtons(node);
  115. } else {
  116. node.querySelectorAll('.post.post_type_reply').forEach(addCopyButtons);
  117. }
  118. }
  119. });
  120. }
  121. }
  122. });
  123. window.addEventListener('load', () => {
  124. processAllPosts();
  125. observer.observe(document.body, { childList: true, subtree: true });
  126. });
  127. })();
  128.  
Add Comment
Please, Sign In to add comment