Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Character Prompt Copy
- // @namespace http://tampermonkey.net/
- // @version 4.0
- // @description Finds <character> tags via regex to copy content, ignoring outside text. Includes append-to-buffer functionality.
- // @author Fey
- // @match *://2ch.hk/wr/*
- // @match *://2ch.life/wr/*
- // @grant GM_setClipboard
- // @grant GM_addStyle
- // @grant GM_setValue
- // @grant GM_getValue
- // ==/UserScript==
- (function() {
- 'use strict';
- // --- 1. Define SVG Icons ---
- 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>`;
- 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>`;
- 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>`;
- // --- 2. Define Local Storage Key & Regex ---
- const STORAGE_KEY = 'tampermonkey_post_compiler_buffer';
- // This regex captures <character ...> ... </character> blocks.
- // It's non-greedy (s*?) to handle multiple blocks in one post correctly.
- const CHARACTER_REGEX = /<character[^>]*>[\s\S]*?<\/character>/g;
- // --- 3. Style the buttons ---
- GM_addStyle(`
- .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; }
- .post-copy-btn:hover { color: #f00; transform: scale(1.1); }
- .post-copy-btn.copied { color: #78b346; cursor: default; }
- .post-copy-btn.copied:hover { transform: none; }
- `);
- // --- 4. Core Logic ---
- const addCopyButtons = (postNode) => {
- if (!postNode || typeof postNode.querySelector !== 'function') return;
- const targetContainer = postNode.querySelector('.post__details');
- if (!targetContainer || targetContainer.querySelector('.post-copy-btn')) return;
- // A helper function to extract text using the new Regex method.
- const getTextToCopy = (messageElement) => {
- if (!messageElement) return '';
- // Get the raw HTML content of the post as a string.
- let postHtml = messageElement.innerText;
- // Use the regex to find all <character> blocks.
- const matches = postHtml.match(CHARACTER_REGEX);
- if (matches && matches.length > 0) {
- // If found, join the matched blocks. This implicitly ignores all other text.
- // Replace <br> tags with newlines for proper formatting in clipboard.
- return matches.join('\n\n').replace(/<br\s*\/?>/gi, '\n');
- } else {
- // If no character tags are found, fall back to copying the visible text.
- return messageElement.innerText.trim();
- }
- };
- // A helper for showing feedback on a button
- const showFeedback = (button, originalIcon, originalTitle) => {
- button.innerHTML = checkIconSVG;
- button.classList.add('copied');
- setTimeout(() => {
- button.innerHTML = originalIcon;
- button.classList.remove('copied');
- button.title = originalTitle;
- }, 1500);
- };
- // --- Create Button 1: Copy/Replace ---
- const copyButton = document.createElement('button');
- copyButton.className = 'post-copy-btn copy-replace-btn';
- copyButton.innerHTML = copyIconSVG;
- copyButton.title = 'Copy (Replace)';
- copyButton.addEventListener('click', (event) => {
- event.preventDefault();
- const messageElement = postNode.querySelector('.post__message');
- const postText = getTextToCopy(messageElement);
- if (postText) {
- GM_setClipboard(postText);
- GM_setValue(STORAGE_KEY, postText); // Also resets the buffer
- copyButton.title = 'Copied! (Buffer reset)';
- showFeedback(copyButton, copyIconSVG, 'Copy (Replace)');
- }
- });
- // --- Create Button 2: Add/Append ---
- const addButton = document.createElement('button');
- addButton.className = 'post-copy-btn copy-append-btn';
- addButton.innerHTML = addIconSVG;
- const updateAddButtonTitle = () => {
- const buffer = GM_getValue(STORAGE_KEY, '');
- const count = buffer ? buffer.split('\n\n---\n\n').length : 0;
- addButton.title = `Append (Buffer: ${count} item${count === 1 ? '' : 's'})`;
- };
- updateAddButtonTitle(); // Set initial title
- addButton.addEventListener('click', (event) => {
- event.preventDefault();
- const messageElement = postNode.querySelector('.post__message');
- const postText = getTextToCopy(messageElement);
- if (postText) {
- let currentBuffer = GM_getValue(STORAGE_KEY, '');
- let newBuffer = currentBuffer ? (currentBuffer + '\n\n---\n\n' + postText) : postText;
- GM_setValue(STORAGE_KEY, newBuffer);
- GM_setClipboard(newBuffer);
- updateAddButtonTitle();
- addButton.title = 'Appended & Copied!';
- showFeedback(addButton, addIconSVG, addButton.title); // Pass updated title back
- }
- });
- targetContainer.appendChild(copyButton);
- targetContainer.appendChild(addButton);
- };
- // --- 5. Execution Logic ---
- const processAllPosts = () => document.querySelectorAll('.post.post_type_reply').forEach(addCopyButtons);
- const observer = new MutationObserver((mutationsList) => {
- for (const mutation of mutationsList) {
- if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
- mutation.addedNodes.forEach(node => {
- if (node.nodeType === 1) {
- if (node.matches('.post.post_type_reply')) {
- addCopyButtons(node);
- } else {
- node.querySelectorAll('.post.post_type_reply').forEach(addCopyButtons);
- }
- }
- });
- }
- }
- });
- window.addEventListener('load', () => {
- processAllPosts();
- observer.observe(document.body, { childList: true, subtree: true });
- });
- })();
Add Comment
Please, Sign In to add comment